home *** CD-ROM | disk | FTP | other *** search
/ ETO Development Tools 1 / ETO Development Tools 1.iso / Tools - Objects / MacApp / MacApp 2.0 CD Release / MacApp 2.0 (Many Libraries) / Examples / DrawShapes / UDrawShapes.inc1.p < prev    next >
Encoding:
Text File  |  1990-03-27  |  75.0 KB  |  3,045 lines  |  [TEXT/MPS ]

  1. {$P}
  2. {[a-,body+,h-,o=100,r+,rec+,t=4,u+,#+,j=20/57/1$,n+]}
  3. { UDrawShapes.inc1.p}
  4. { Copyright © 1985 - 1990 by Apple Computer, Inc.  All rights reserved.}
  5.  
  6. CONST
  7.     kRainbowArrow        = 140;
  8.  
  9.     kPaletteWidth        = 41;                            {Width of the palette}
  10.  
  11.     kMinWidth            = 20;                            {minimum width of new shapes}
  12.     kMinHeight            = 20;                            {minimum height of new shapes}
  13.     { The above two constants define the minimum size a newly-sketched shape
  14.       must become before it is considered a legitimate attempt to draw }
  15.  
  16.     kStaggerAmount        = 16;                            {Amount to stagger windows by}
  17.  
  18.     kColorMenuBar        = 131;                            {Menu bar for a color system}
  19.     kNonColorMenuBar    = 132;                            {Menu bar for a black & white system}
  20.  
  21.     kPickerPrompt        = 256;                            {'STR ' resource for Color Picker}
  22.  
  23.     cChangeShade        = 1012;                         {Buzz command for "Undo Shade Change"}
  24.     cChangeColor        = 1013;                         {Buzz command for "Undo Color Change"}
  25.  
  26. VAR
  27.     gPat:                ARRAY [cWhite..cBlack] OF Pattern;
  28.  
  29.     { prototype shapes for the palette }
  30.  
  31.     gShapesArray:        ARRAY [1..kShapesInPalette] OF TShape;
  32.  
  33.     { bounds of each square in palette }
  34.     gChoiceArray:        ARRAY [0..kShapesInPalette] OF Rect;
  35.  
  36.     gArwBitMap:         BitMap;                         {bitmap used to draw the arrow in palette}
  37.  
  38.     gClipMargin:        Point;                            {the top & left margins to use when
  39.                                                          displaying shapes in the Clipboard}
  40.     gPasteReplacesSelection: BOOLEAN;                    {Tells whether PASTE should REPLACE the
  41.                                                          existing selection, or instead simply add
  42.                                                          new shapes without replacement. Default:
  43.                                                          FALSE; change its value by using the "More
  44.                                                          Debug" menu, obtainable by typing
  45.                                                          command-D}
  46.  
  47.     gConstrainDrags:    BOOLEAN;                        {Whether dragging shapes with the mouse
  48.                                                          should be constrained so that nothing
  49.                                                          overlaps the view's borders. Default:
  50.                                                          TRUE; change its value by using the "More
  51.                                                          Debug" menu, obtainable by typing
  52.                                                          command-D}
  53.  
  54.     gStaggerCount:        INTEGER;                        {For SimpleStagger}
  55.  
  56.     gRainbowArrow:        CCrsrHandle;
  57.  
  58.     gShadeMenu:         TShadeMenu;
  59.  
  60.     gBetterFeedback:    BOOLEAN;                        { TRUE to invoke BetterFeedback routines }
  61.  
  62. {--------------------------------------------------------------------------------------------------}
  63.     {$S AInit}
  64.  
  65. PROCEDURE TShapeApplication.IShapeApplication;            {Initialize the application}
  66.  
  67.     VAR
  68.         r:                    Rect;
  69.         box:                TBox;
  70.         circle:             TCircle;
  71.         hBox:                THeavyBox;
  72.         top:                INTEGER;
  73.         i:                    INTEGER;
  74.         ShadeMenu:            TShadeMenu;
  75.  
  76.     BEGIN
  77.  
  78.     IF qNeedsColorQD | gConfiguration.hasColorQD THEN
  79.         gMBarDisplayed := kColorMenuBar
  80.     ELSE
  81.         gMBarDisplayed := kNonColorMenuBar;
  82.  
  83.     IApplication(kDocType);                             {Generic initialization}
  84.  
  85.     gPat[cWhite] := White;                                {Fill the global array of patterns}
  86.     gPat[cLtGray] := LtGray;
  87.     gPat[cGray] := Gray;
  88.     gPat[cDkGray] := DkGray;
  89.     gPat[cBlack] := Black;
  90.  
  91.     { Install our custom pattern menu }
  92.     New(ShadeMenu);
  93.     FailNil(ShadeMenu);
  94.     ShadeMenu.IShadeMenu;
  95.     gShadeMenu := ShadeMenu;
  96.  
  97.     { Set the standard margins to use in the clipboard }
  98.     SetPt(gClipMargin, 16, 16);
  99.  
  100.     SetRect(r, 10, 50, 28, 70);                         {Define the prototype shapes}
  101.     New(box);
  102.     FailNil(box);
  103.     box.IBox(r, IDBox);
  104.     gShapesArray[IDBox] := box;
  105.  
  106.     OffSetRect(r, 0, 40);
  107.     New(circle);
  108.     FailNil(circle);
  109.     circle.ICircle(r, IDCircle);
  110.     gShapesArray[IDCircle] := circle;
  111.  
  112.     OffSetRect(r, 0, 40);
  113.     New(hBox);
  114.     FailNil(hBox);
  115.     hBox.IHeavyBox(r, IDhBox);
  116.     gShapesArray[IDhBox] := hBox;
  117.  
  118.     WITH gArwBitMap DO                                    {Define the arrow bitmap to be drawn in the
  119.                                                          palette}
  120.         BEGIN
  121.         rowBytes := 2;
  122.         SetRect(bounds, 0, 0, 16, 16);
  123.         baseAddr := @arrow.data;
  124.         END;
  125.  
  126.     top := 0;
  127.     FOR i := 0 TO kShapesInPalette DO                    {Define the palette choices}
  128.         BEGIN
  129.         SetRect(r, 0, top, kPaletteWidth - 1, top + kPaletteWidth - 1);
  130.         gChoiceArray[i] := r;
  131.         top := top + kPaletteWidth - 1;
  132.         END;
  133.  
  134.     gPasteReplacesSelection := FALSE;
  135.     gConstrainDrags := TRUE;
  136.     gStaggerCount := 0;
  137.  
  138.     IF qNeedsColorQD | gConfiguration.hasColorQD THEN
  139.         BEGIN
  140.         { Unlike GetCursor, GetCCursor makes a copy of the color cursor
  141.           resource.  Therefore, you should make one call to GetCCursor
  142.           and multiple calls to SetCCursor }
  143.         gRainbowArrow := GetCCursor(kRainbowArrow);
  144.         FailNil(gRainbowArrow);
  145.         END;
  146.     gBetterFeedback := kBetterFeedbackDesired;
  147.  
  148.     IF qTemplateViews & gDeadStripSuppression THEN
  149.         BEGIN
  150.         IF Member(TObject(NIL), TShapeView) THEN;
  151.         IF Member(TObject(NIL), TPalette) THEN;
  152.         END;
  153.     END;
  154.  
  155. {--------------------------------------------------------------------------------------------------}
  156. {$IFC qDebug}
  157. {$S ASelCommand}
  158.  
  159. FUNCTION TShapeApplication.DoCommandKey(ch: CHAR; VAR info: EventInfo): TCommand; OVERRIDE;
  160.  
  161. { This illustrates how to have a 'Command-key-only' command, i.e. a command
  162.   which is NOT in a menu, but rather only available when the user types
  163.   the 'command' key and another key concurrently.  In this example, the
  164.   user presses 'Command-D' (the D can be in upper or lower case) to
  165.   request that the special 'more debug' menu be put up (or be taken down
  166.   if it was already up) }
  167.  
  168.     BEGIN
  169.     IF ((ch = 'D') | (ch = 'd')) THEN
  170.         DoCommandKey := DoMenuCommand(cCmdDTyped)
  171.     ELSE
  172.         DoCommandKey := INHERITED DoCommandKey(ch, info);
  173.     END;
  174. {$ENDC}
  175.  
  176. {--------------------------------------------------------------------------------------------------}
  177. {$S AOpen}
  178.  
  179. FUNCTION TShapeApplication.DoMakeDocument(itsCmdNumber: CmdNumber): TDocument;
  180. { NB: Not used to create the document for a shape view in the Clipboard }
  181.  
  182.     VAR
  183.         shapeDocument:        TShapeDocument;
  184.  
  185.     BEGIN
  186.     New(shapeDocument);
  187.     FailNil(shapeDocument);
  188.     shapeDocument.IShapeDocument(kDocType);
  189.     DoMakeDocument := shapeDocument;
  190.     END;
  191.  
  192. {--------------------------------------------------------------------------------------------------}
  193. {$IFC qDebug}
  194. {$S ASelCommand}
  195.  
  196. FUNCTION TShapeApplication.DoMenuCommand(aCmdNumber: CmdNumber): TCommand; OVERRIDE;
  197.  
  198.     BEGIN
  199.     DoMenuCommand := NIL;
  200.     CASE aCmdNumber OF
  201.  
  202.         cPasteReplacesSelection:
  203.             gPasteReplacesSelection := NOT gPasteReplacesSelection;
  204.  
  205.         cConstrainDrags:
  206.             gConstrainDrags := NOT gConstrainDrags;
  207.  
  208.         cCmdDTyped:                                     {Command-D typed by user}
  209.             BEGIN
  210.             IF GetMHandle(mMoreDebug) = NIL THEN        {menu not currrently up--put it up}
  211.                 InsertMenu(GetResMenu(mMoreDebug), 0)
  212.             ELSE                                        {menu currently up -- take it down}
  213.                 DeleteMenu(mMoreDebug);
  214.             InvalidateMenuBar;                            { Get it redrawn }
  215.             END;
  216.  
  217.         OTHERWISE
  218.             DoMenuCommand := INHERITED DoMenuCommand(aCmdNumber);
  219.     END;                                                {Case}
  220.     END;
  221. {$ENDC}
  222.  
  223. {--------------------------------------------------------------------------------------------------}
  224. {$IFC qDebug}
  225. {$S ARes}
  226.  
  227. PROCEDURE TShapeApplication.DoSetupMenus; OVERRIDE;
  228. {The only menu commands handled here are the following two debugging commands:}
  229.  
  230.     BEGIN
  231.     INHERITED DoSetupMenus;
  232.     EnableCheck(cPasteReplacesSelection, TRUE, gPasteReplacesSelection);
  233.     EnableCheck(cConstrainDrags, TRUE, gConstrainDrags);
  234.     END;
  235. {$ENDC}
  236.  
  237. {--------------------------------------------------------------------------------------------------}
  238. {$IFC qDebug}
  239. {$S ADebug}
  240.  
  241. PROCEDURE TShapeApplication.IdentifySoftware; OVERRIDE;
  242.  
  243.     BEGIN
  244.     WriteLn('DrawShapes Source date: 6 June 86; Compiled on: ', COMPDATE, ' @ ', COMPTIME);
  245.     INHERITED IdentifySoftware;
  246.     END;
  247. {$ENDC}
  248.  
  249. {--------------------------------------------------------------------------------------------------}
  250. {$S AClipboard}
  251.  
  252. FUNCTION TShapeApplication.MakeViewForAlienClipboard: TView; OVERRIDE;
  253. { Launch a view to represent the data found in the Clipboard at
  254.   application start-up time, or when returning from an excursion
  255.   to Switcher, or when returning from a Desk Accessory }
  256.  
  257.     VAR
  258.         offset:             LONGINT;
  259.         clipShapeView:        TShapeView;
  260.         clipShapeDoc:        TShapeDocument;
  261.         clipShapes:         ShapesOnClipboard;
  262.         aNewShape:            TShape;
  263.         i:                    INTEGER;
  264.         err:                LONGINT;
  265.         perm:                BOOLEAN;
  266.         fi:                 FailInfo;
  267.  
  268.     PROCEDURE HdlFailure(error: OSErr; message: LONGINT);
  269.  
  270.         BEGIN
  271.         Handle(clipShapes) := DisposeIfHandle(clipShapes);
  272.  
  273.         FreeIfObject(clipShapeDoc);
  274.         clipShapeDoc := NIL;
  275.         END;
  276.  
  277.     BEGIN
  278.     clipShapes := NIL;
  279.  
  280.     {Before doing anything else, make sure the scrap contains shapes}
  281.     IF GetScrap(NIL, kShapeClipType, offset) > 0 THEN    {found my kind of data }
  282.         BEGIN
  283.         New(clipShapeDoc);
  284.         FailNil(clipShapeDoc);
  285.         clipShapeDoc.IShapeDocument(kDocType);
  286.  
  287.         CatchFailures(fi, HdlFailure);
  288.         New(clipShapeView);
  289.         FailNil(clipShapeView);
  290.         clipShapeView.IShapeView(clipShapeDoc, NIL, TRUE);
  291.  
  292.         clipShapeDoc.fShapeView := clipShapeView;
  293.  
  294.         clipShapes := ShapesOnClipboard(NewPermHandle(0));
  295.         FailNil(clipShapes);
  296.         FailSpaceIsLow;
  297.  
  298.         perm := PermAllocation(TRUE);                    {Don't allow GetScrap to use temp space}
  299.         err := GetScrap(Handle(clipShapes), kShapeClipType, offset);
  300.         perm := PermAllocation(perm);                    {Restore perm allocation setting}
  301.  
  302.   { Only a negative result indicates an error--FailOSErr considers
  303.     any non-zero result an error.}
  304.         IF err < 0 THEN
  305.             FailOSErr(err);
  306.  
  307.         FOR i := 0 TO clipShapes^^.theNumberOfShapes - 1 DO
  308.             BEGIN
  309.             aNewShape := TShape(gShapesArray[clipShapes^^.theShapes[i].theId].Clone);
  310.             FailNil(aNewShape);
  311.             WITH aNewShape, clipShapes^^.theShapes[i] DO
  312.                 BEGIN
  313.                 fShade := theShade;
  314.                 fColor := theColor;
  315.                 fExtentRect := theRect;
  316.                 END;
  317.             clipShapeDoc.AddShape(aNewShape);
  318.             END;
  319.  
  320.         Success(fi);
  321.         Handle(clipShapes) := DisposeIfHandle(clipShapes);
  322.  
  323.         MakeViewForAlienClipboard := clipShapeView;
  324.         END
  325.     ELSE
  326.         MakeViewForAlienClipboard := INHERITED MakeViewForAlienClipboard;
  327.     END;
  328.  
  329. {--------------------------------------------------------------------------------------------------}
  330. {$S AOpen}
  331.  
  332. PROCEDURE TShapeDocument.IShapeDocument(fileType: OSType);
  333.  
  334.     VAR
  335.         fi:                 FailInfo;
  336.  
  337.     PROCEDURE HdlNewList(error: OSErr; message: LONGINT);
  338.  
  339.         BEGIN
  340.         Free;
  341.         END;
  342.  
  343.     BEGIN
  344.     fShapeView := NIL;
  345.     fPaletteView := NIL;
  346.     fShapeList := NIL;                                    {Just in case IDocument fails}
  347.     IDocument(fileType, kDocType, kUsesDataFork, kUsesRsrcFork, NOT kDataOpen, NOT kRsrcOpen);
  348.  
  349.     CatchFailures(fi, HdlNewList);                        { In case NewList fails.}
  350.     fShapeList := NewList;
  351.     Success(fi);
  352.  
  353.     {$IFC qDebug}
  354.     fShapeList.SetEltType('TShape');
  355.     {$ENDC}
  356.  
  357.     fSavePrintInfo := TRUE;
  358.  
  359.     fReopening := FALSE;
  360.     fFiltering := FALSE;
  361.     fReplaceCommand := NIL;
  362.     END;
  363.  
  364. {--------------------------------------------------------------------------------------------------}
  365. {$S ARes}
  366.  
  367. PROCEDURE TShapeDocument.AddShape(shape: TShape);
  368.  
  369.     BEGIN
  370.     fShapeList.InsertLast(shape);
  371.     END;
  372.  
  373. {--------------------------------------------------------------------------------------------------}
  374. {$S ARes}
  375.  
  376. PROCEDURE TShapeDocument.DeleteShape(shape: TShape);
  377. { Doesn't work for shape still belonging to a command
  378.   (i.e., not yet committed to the document }
  379.  
  380.     BEGIN
  381.     fShapeList.Delete(shape);
  382.     FreeIfObject(shape);
  383.     shape := NIL;
  384.     END;
  385.  
  386. {--------------------------------------------------------------------------------------------------}
  387. {$S AOpen}
  388.  
  389. PROCEDURE TShapeDocument.DoMakeViews(forPrinting: BOOLEAN);
  390.  
  391.     VAR
  392.         shapeView:            TShapeView;
  393.         palette:            TPalette;
  394.         aWindow:            TWindow;
  395.         aDocState:            DocState;
  396.         minSize:            Point;
  397.         maxSize:            Point;
  398.  
  399.     PROCEDURE CreateProceduralShapeView;
  400.         { CreateProceduralShapeView used when creating views procedurally to create the shapes view
  401.         in both printing & non-printing cases }
  402.  
  403.         BEGIN
  404.         New(shapeView);
  405.         FailNil(shapeView);
  406.         shapeView.IShapeView(SELF, palette, FALSE);
  407.         
  408.         fShapeView := shapeView;
  409.         END;
  410.  
  411.     PROCEDURE RestoreWindow;
  412.         { RestoreWindow restores the window & scroller using the settings in the documents fDocState
  413.         field }
  414.  
  415.         BEGIN
  416.         aDocState := fDocState;
  417.         WITH aDocState.theWindowRect DO
  418.             BEGIN
  419.             aWindow.Resize(right - left, bottom - top, FALSE);
  420.             aWindow.Locate(left, top, FALSE);
  421.             END;
  422.         aWindow.ForceOnScreen;
  423.         WITH aDocState.theScrollPosition DO
  424.             fShapeView.fScroller.ScrollTo(h, v, FALSE);
  425.         END;
  426.  
  427.     BEGIN
  428.     IF forPrinting THEN
  429.         BEGIN
  430.         IF qTemplateViews THEN
  431.             BEGIN
  432.             shapeView := TShapeView(DoCreateViews(SELF, NIL, kShapeViewRSRCID, gZeroVPt));
  433.             fShapeView := shapeView;
  434.             END
  435.         ELSE
  436.             BEGIN
  437.             palette := NIL;
  438.             CreateProceduralShapeView;
  439.             END;
  440.         END                                             { this is the end of the "forPrinting=TRUE"
  441.                                                          case }
  442.     ELSE
  443.         BEGIN
  444.         IF qTemplateViews THEN
  445.             BEGIN
  446.             aWindow := NewTemplateWindow(kShapeWindowRSRCID, SELF);
  447.             FailNil(aWindow);
  448.  
  449.             fPaletteView := TPalette(aWindow.FindSubView('PLTT'));
  450.             FailNil(fPaletteView);
  451.  
  452.             fShapeView := TShapeView(aWindow.FindSubView('SHAP'));
  453.             FailNil(fShapeView);
  454.  
  455.             fShapeView.fPalette := fPaletteView;
  456.             END
  457.         ELSE
  458.             BEGIN
  459.             New(palette);
  460.             FailNil(palette);
  461.  
  462.             palette.IPalette(SELF);
  463.             fPaletteView := palette;
  464.  
  465.             CreateProceduralShapeView;
  466.  
  467.             aWindow := NewPaletteWindow(kShapeWindowRSRCID, kWantHScrollBar, kWantVScrollBar, SELF,
  468.                                         fShapeView, fPaletteView, kPaletteWidth, kLeftPalette);
  469.             END;
  470.  
  471.         fShapeView.fScroller := fShapeView.GetScroller(TRUE);
  472.  
  473.         IF fReopening THEN
  474.             RestoreWindow
  475.         ELSE
  476.             BEGIN
  477.             aWindow.AdaptToScreen;
  478.             aWindow.SimpleStagger(kStaggerAmount, kStaggerAmount, gStaggerCount);
  479.             END;
  480.  
  481.         { set window's resize limits so it can't become wider than the shapeview's edge }
  482.         WITH aWindow.fResizeLimits DO
  483.             BEGIN
  484.             minSize := topLeft;
  485.             maxSize := botRight;
  486.             END;
  487.         WITH maxSize DO
  488.             h := Min(fShapeView.fSize.h + fPaletteView.fSize.h + kSBarSizeMinus1, h);
  489.         aWindow.SetResizeLimits(minSize, maxSize);
  490.         END;
  491.     END;
  492.  
  493. {--------------------------------------------------------------------------------------------------}
  494. {$S AWriteFile}
  495.  
  496. PROCEDURE TShapeDocument.DoNeedDiskSpace(VAR dataForkBytes, rsrcForkBytes: LONGINT);
  497.  
  498.     BEGIN
  499.     { get Print record requirements }
  500.     INHERITED DoNeedDiskSpace(dataForkBytes, rsrcForkBytes);
  501.  
  502.     dataForkBytes := dataForkBytes + fShapeList.GetSize * SIZEOF(ShapeData);
  503.     rsrcForkBytes := rsrcForkBytes + kRsrcTypeOverhead + kRsrcOverhead + SIZEOF(DocState);
  504.     END;
  505.  
  506. {
  507. Doc file has the following format:
  508.   Data Fork:
  509.     (a)  kSizePrintInfo (120 bytes) => PrintInfo
  510.     (b)  The rest => The shapes themselves
  511.  
  512.  Resource Fork:
  513.     (a)  SIZEOF(DocState) => DocState (rsrc type: 'DSTA', number: 1)
  514. }
  515.  
  516. {--------------------------------------------------------------------------------------------------}
  517. {$S AReadFile}
  518.  
  519. PROCEDURE TShapeDocument.DoRead(aRefNum: INTEGER; rsrcExists, forPrinting: BOOLEAN);
  520.  
  521.     VAR
  522.         i:                    INTEGER;
  523.         id:                 INTEGER;
  524.         count:                LONGINT;
  525.         newShape:            TShape;
  526.         docStateHandle:     HDocState;
  527.  
  528.     BEGIN
  529.     INHERITED DoRead(aRefNum, rsrcExists, forPrinting); {read print info stuff}
  530.  
  531.     IF rsrcExists THEN
  532.         BEGIN
  533.         docStateHandle := HDocState(GetResource(kDocRsrcKind, kDocStateID));
  534.         IF docStateHandle <> NIL THEN
  535.             BEGIN
  536.             fDocState := docStateHandle^^;
  537.             fReopening := TRUE;
  538.             END;
  539.         END
  540.     ELSE
  541.         BEGIN
  542.         {$IFC qDebug}
  543.         ProgramBreak('Resource fork doesn''t exist for saved file');
  544.         {$ENDC}
  545.         Failure(1                                        {???} , 0);
  546.         END;
  547.  
  548.     FOR i := 1 TO fDocState.theNumberOfShapes DO
  549.         BEGIN
  550.         count := SIZEOF(INTEGER);
  551.         FailOSErr(FSRead(aRefNum, count, @id));
  552.  
  553.         IF (id >= 1) & (id <= kShapesInPalette) THEN
  554.             BEGIN
  555.             newShape := TShape(gShapesArray[id].Clone);
  556.             FailNil(newShape);
  557.  
  558.             newShape.ReadFrom(aRefNum);
  559.  
  560.             AddShape(newShape);
  561.             END
  562.             {$IFC qDebug}
  563.         ELSE
  564.             WriteLn('Ignored invalid shape ID = ', id: 1, ' shape #: ', i: 1)
  565.             {$ENDC} ;
  566.         END;
  567.     END;
  568.  
  569. {--------------------------------------------------------------------------------------------------}
  570. {$S AWriteFile}
  571.  
  572. PROCEDURE TShapeDocument.DoWrite(aRefNum: INTEGER; makingCopy: BOOLEAN);
  573.  
  574.     VAR
  575.         vhs:                VHSelect;
  576.         aDocState:            DocState;
  577.         count:                LONGINT;
  578.         window:             TWindow;
  579.         numberOfShapes:     INTEGER;
  580.         dummyRect:            Rect;
  581.         docStateHandle:     HDocState;
  582.  
  583. {--------------------------------------------------------------------------------------------------}
  584.  
  585.     PROCEDURE WriteShape(shape: TShape);
  586.  
  587.         BEGIN
  588.         shape.WriteTo(aRefNum);
  589.         END;
  590.  
  591.     BEGIN
  592.     INHERITED DoWrite(aRefNum, makingCopy);             {write print info stuff}
  593.  
  594.     { Call SurveyShapes just to get number of shapes }
  595.     SurveyShapes(FALSE, numberOfShapes, dummyRect);
  596.     window := TWindow(fWindowList.First);
  597.  
  598.     docStateHandle := HDocState(NewHandle(SIZEOF(DocState)));
  599.     FailNil(docStateHandle);
  600.  
  601.     WITH docStateHandle^^ DO
  602.         BEGIN
  603.         window.GetGlobalBounds(theWindowRect);
  604.         theScrollPosition := fShapeView.fScroller.fTranslation;
  605.         theNumberOfShapes := numberOfShapes;
  606.         END;
  607.  
  608.     AddResource(Handle(docStateHandle), kDocRsrcKind, kDocStateID, 'Doc State');
  609.     FailOSErr(ResError);
  610.  
  611.     EachVirtualShapeDo(WriteShape);
  612.     END;
  613.  
  614. {--------------------------------------------------------------------------------------------------}
  615. {$S ARes}
  616.  
  617. PROCEDURE TShapeDocument.EachShapeDo(PROCEDURE DoThis(shape: TShape));
  618. { Iterates through the list of shapes. We have a separate method for this
  619.   to hide the actual implementation of the shape list structure. }
  620.  
  621.     BEGIN
  622.     fShapeList.Each(DoThis);
  623.     END;
  624.  
  625. {--------------------------------------------------------------------------------------------------}
  626. {$S ARes}
  627.  
  628. PROCEDURE TShapeDocument.EachPotentialShapeDo(PROCEDURE
  629.                                               DoThis(shape: TShape));
  630. { Iterates through all the shapes in document plus any 'pastee' shapes
  631.   which may have been added by a not-yet-committed Paste.}
  632.  
  633.     BEGIN
  634.     EachShapeDo(DoThis);
  635.     IF fReplaceCommand <> NIL THEN
  636.         fReplaceCommand.EachNewShapeDo(DoThis);
  637.     END;
  638.  
  639. {--------------------------------------------------------------------------------------------------}
  640. {$S ARes}
  641.  
  642. PROCEDURE TShapeDocument.EachVirtualShapeDo(PROCEDURE
  643.                                             DoThis(shape: TShape));
  644. { EachVirtualShape iterates through only those shapes that appear
  645.   to be present at the moment to the USER, given the
  646.   UNDO/REDO status of the last command. Thus it iterates
  647.   through some but possibly not all of the shapes in the
  648.   document, and possibly also through not-yet-in-the-document pastees }
  649.  
  650. {--------------------------------------------------------------------------------------------------}
  651.  
  652.     PROCEDURE MaybeDoThis(shape: TShape);
  653.  
  654.         BEGIN
  655.         IF (NOT fFiltering) | (NOT shape.fWasSelected) THEN
  656.             DoThis(shape);
  657.         END;
  658.  
  659.     BEGIN
  660.     EachShapeDo(MaybeDoThis);
  661.     IF fReplaceCommand <> NIL THEN
  662.         fReplaceCommand.EachNewShapeDo(DoThis);
  663.     END;
  664.  
  665. {--------------------------------------------------------------------------------------------------}
  666. {$S AFields}
  667.  
  668. PROCEDURE TShapeDocument.Fields(PROCEDURE DoToField(fieldName: Str255; fieldAddr: Ptr;
  669.                                                     fieldType: INTEGER)); OVERRIDE;
  670.  
  671.     BEGIN
  672.     DoToField('TShapeDocument', NIL, bClass);
  673.     DoToField('fShapeView', @fShapeView, bObject);
  674.     DoToField('fPaletteView', @fPaletteView, bObject);
  675.     DoToField('fShapeList', @fShapeList, bObject);
  676.     DoToField('fDocState.theNumberOfShapes', @fDocState.theNumberOfShapes, bInteger);
  677.     DoToField('fDocState.theWindowRect', @fDocState.theWindowRect, bRect);
  678.     DoToField('fDocState.theScrollPosition', @fDocState.theScrollPosition, bPoint);
  679.     DoToField('fReopening', @fReopening, bBoolean);
  680.     DoToField('fReplaceCommand', @fReplaceCommand, bObject);
  681.     DoToField('fFiltering', @fFiltering, bBoolean);
  682.     INHERITED Fields(DoToField);
  683.     END;
  684.  
  685. {--------------------------------------------------------------------------------------------------}
  686. {$S ARes}
  687.  
  688. FUNCTION TShapeDocument.FirstSelectedShapeThat(FUNCTION
  689.                                                TestSelectedShape(aShape: TShape): BOOLEAN): TShape;
  690.  
  691.     VAR
  692.         aShape:             TShape;
  693.  
  694.     FUNCTION TestShape(aShape: TObject): BOOLEAN;
  695.  
  696.         BEGIN
  697.         IF TShape(aShape).fIsSelected THEN
  698.             TestShape := TestSelectedShape(TShape(aShape))
  699.         ELSE
  700.             TestShape := FALSE;
  701.         END;
  702.  
  703.     BEGIN
  704.     aShape := TShape(fShapeList.FirstThat(TestShape));
  705.     IF (aShape = NIL) & (fReplaceCommand <> NIL) THEN
  706.         aShape := fReplaceCommand.FirstShapeThat(TestSelectedShape);
  707.     FirstSelectedShapeThat := aShape;
  708.     END;
  709.  
  710. {--------------------------------------------------------------------------------------------------}
  711. {$S AClose}
  712.  
  713. PROCEDURE TShapeDocument.Free; OVERRIDE;
  714.  
  715.     BEGIN
  716.     FreeData;
  717.     FreeIfObject(fShapeList);
  718.     fShapeList := NIL;
  719.  
  720.     INHERITED Free;
  721.     END;
  722.  
  723. {--------------------------------------------------------------------------------------------------}
  724. {$S AClose}
  725.  
  726. PROCEDURE TShapeDocument.FreeData;
  727.  
  728.     PROCEDURE DoToShape(aShape: TShape);
  729.  
  730.         BEGIN
  731.         FreeIfObject(aShape);
  732.         END;
  733.  
  734.     BEGIN
  735.     IF fShapeList <> NIL THEN
  736.         BEGIN
  737.         EachShapeDo(DoToShape);
  738.         fShapeList.DeleteAll;
  739.         END;
  740.     END;
  741.  
  742. {--------------------------------------------------------------------------------------------------}
  743. {$S ARes}
  744.  
  745. PROCEDURE TShapeDocument.SurveyShapes(selecteesOnly: BOOLEAN; VAR numberOfShapes: INTEGER;
  746.                                       VAR combinedExtent: Rect);
  747.  
  748.     PROCEDURE UnionizeShapes(shape: TShape);
  749.  
  750.         BEGIN
  751.         IF shape.fIsSelected | NOT selecteesOnly THEN
  752.             BEGIN
  753.             numberOfShapes := numberOfShapes + 1;
  754.             IF numberOfShapes > 1 THEN
  755.             {$Push} {$h-}
  756.                 UnionRect(shape.fExtentRect, combinedExtent, combinedExtent)
  757.                 {$Pop}
  758.  
  759.             ELSE
  760.                 combinedExtent := shape.fExtentRect;
  761.             END;
  762.         END;
  763.  
  764.     BEGIN
  765.     numberOfShapes := 0;
  766.     combinedExtent := gZeroRect;
  767.     EachVirtualShapeDo(UnionizeShapes);
  768.     END;
  769.  
  770. {--------------------------------------------------------------------------------------------------}
  771. {$S AOpen}
  772.  
  773. PROCEDURE TPalette.IPalette(itsDocument: TDocument);
  774.  
  775.     VAR
  776.         itsSize:            VPoint;
  777.  
  778.     BEGIN
  779.     SetVPt(itsSize, kPaletteWidth, 0);
  780.     IView(itsDocument, NIL, gZeroVPt, itsSize, sizeFixed, sizeSuperView);
  781.  
  782.     fCurrShape := 0;
  783.     END;
  784.  
  785. {--------------------------------------------------------------------------------------------------}
  786. {$IFC qTemplateViews}
  787. {$S AOpen}
  788.  
  789. PROCEDURE TPalette.IRes(itsDocument: TDocument; itsSuperview: TView; VAR itsParams: Ptr); OVERRIDE;
  790.  
  791.     BEGIN
  792.     INHERITED IRes(itsDocument, itsSuperview, itsParams);
  793.  
  794.     fCurrShape := 0;
  795.     END;
  796.  
  797. {$ENDC}
  798.  
  799. {--------------------------------------------------------------------------------------------------}
  800. {$S ARes}
  801.  
  802. PROCEDURE TPalette.DoHighlightSelection(fromHL, toHL: HLState);
  803.  
  804.     VAR
  805.         r:                    Rect;
  806.  
  807.     BEGIN
  808.     IF (fromHL <> toHL) & (fromHL + toHL <> hlOffDim) THEN
  809.         BEGIN
  810.         r := gChoiceArray[fCurrShape];
  811.         UseSelectionColor;
  812.         InvertRect(r);
  813.         END;
  814.     END;
  815.  
  816. {--------------------------------------------------------------------------------------------------}
  817. {$S ASelCommand}
  818.  
  819. FUNCTION TPalette.DoMouseCommand(VAR theMouse: Point; VAR info: EventInfo;
  820.                                  VAR hysteresis: Point): TCommand;
  821.  
  822.     VAR
  823.         i:                    INTEGER;
  824.  
  825.     BEGIN
  826.     DoMouseCommand := NIL;
  827.  
  828.     i := 0;
  829.     REPEAT
  830.         IF PtInRect(theMouse, gChoiceArray[i]) THEN
  831.             LEAVE;
  832.         i := i + 1;
  833.     UNTIL i > kShapesInPalette;
  834.  
  835.     IF (i <= kShapesInPalette) & (i <> fCurrShape) THEN
  836.         BEGIN
  837.         IF Focus THEN
  838.             DoHighlightSelection(hlOn, hlOff);
  839.         fCurrShape := i;
  840.         IF Focus THEN
  841.             DoHighlightSelection(hlOff, hlOn);
  842.         END;
  843.     END;
  844.  
  845. {--------------------------------------------------------------------------------------------------}
  846. {$S ARes}
  847.  
  848. FUNCTION TPalette.DoSetCursor(localPoint: Point; cursorRgn: RgnHandle): BOOLEAN; OVERRIDE;
  849.  
  850.     VAR
  851.         qdExtent:            Rect;
  852.  
  853.     BEGIN
  854.     SetCursor(arrow);
  855.     GetQDExtent(qdExtent);
  856.     RectRgn(cursorRgn, qdExtent);
  857.     DoSetCursor := TRUE;
  858.     END;
  859.  
  860. {--------------------------------------------------------------------------------------------------}
  861. {$S ARes}
  862.  
  863. PROCEDURE TPalette.Draw(area: Rect);
  864.  
  865.     VAR
  866.         i:                    INTEGER;
  867.         r:                    Rect;
  868.  
  869.     BEGIN
  870.     PenSize(1, 1);
  871.     MoveTo(fSize.h - 1, 0);
  872.     Line(0, fSize.v);
  873.     FOR i := 0 TO kShapesInPalette DO
  874.         BEGIN
  875.         FrameRect(gChoiceArray[i]);
  876.  
  877.         IF i = 0 THEN
  878.             BEGIN
  879.             SetRect(r, 14, 12, 30, 28);
  880.             CopyBits(gArwBitMap, thePort^.portBits, gArwBitMap.bounds, r, srcOR, NIL);
  881.             END
  882.         ELSE
  883.             gShapesArray[i].Draw;
  884.         END;
  885.  
  886.     INHERITED Draw(area);
  887.     END;
  888.  
  889. {--------------------------------------------------------------------------------------------------}
  890. {$S AFields}
  891.  
  892. PROCEDURE TPalette.Fields(PROCEDURE DoToField(fieldName: Str255; fieldAddr: Ptr;
  893.                           fieldType: INTEGER)); OVERRIDE;
  894.  
  895.     BEGIN
  896.     DoToField('TPalette', NIL, bClass);
  897.     DoToField('fCurrShape', @fCurrShape, bInteger);
  898.     INHERITED Fields(DoToField);
  899.     END;
  900.  
  901. {--------------------------------------------------------------------------------------------------}
  902. {$S AOpen}
  903.  
  904. PROCEDURE TShapeView.IShapeView(itsDocument: TShapeDocument; itsPalette: TPalette;
  905.                                 forClipboard: BOOLEAN);
  906.  
  907.     VAR
  908.         itsLocation:        VPoint;
  909.         itsSize:            VPoint;
  910.         aHandler:            TStdPrintHandler;
  911.         aDocState:            DocState;
  912.         sd:                 SizeDeterminer;
  913.  
  914.     BEGIN
  915.     fDragging := FALSE;
  916.     fPalette := itsPalette;
  917.     SetVPt(itsSize, kMaxCoord, kMaxCoord);
  918.     IF forClipboard THEN
  919.         sd := sizeVariable
  920.     ELSE
  921.         sd := sizeFillPages;
  922.     IView(itsDocument, NIL, gZeroVPt, itsSize, sd, sd);
  923.     fScroller := NIL;
  924.  
  925.     fShapeDocument := itsDocument;
  926.  
  927.     {$IFC FALSE}                                        {!!! Need to handle this}
  928.     IF forClipboard THEN
  929.         fWouldMakePICTScrap := TRUE;
  930.     {$ENDC}
  931.  
  932.     IF NOT forClipboard THEN
  933.         BEGIN
  934.         New(aHandler);
  935.         FailNil(aHandler);
  936.         aHandler.IStdPrintHandler(itsDocument, SELF, NOT kSquareDots, { does not have square dots }
  937.                                   kFixedSize,            { horizontal page size is fixed }
  938.                                   kFixedSize);            { vertical page size is fixed }
  939.         END;
  940.  
  941.     fClickPt := gZeroPt;                                {plausible starting value}
  942.     END;
  943.  
  944. {--------------------------------------------------------------------------------------------------}
  945. {$IFC qTemplateViews}
  946. {$S AOpen}
  947.  
  948. PROCEDURE TShapeView.IRes(itsDocument: TDocument; itsSuperview: TView;
  949.     VAR itsParams: Ptr); OVERRIDE;
  950.  
  951.     VAR
  952.         aHandler:            TStdPrintHandler;
  953.  
  954.     BEGIN
  955.     fDragging := FALSE;
  956.     fScroller := NIL;
  957.     fPalette := NIL;
  958.  
  959.     INHERITED IRes(itsDocument, itsSuperview, itsParams);
  960.  
  961.     fShapeDocument := TShapeDocument(itsDocument);
  962.  
  963.     New(aHandler);
  964.     FailNil(aHandler);
  965.     aHandler.IStdPrintHandler(itsDocument, SELF, NOT kSquareDots, { does not have square dots }
  966.                               kFixedSize,                { horizontal page size is fixed }
  967.                               kFixedSize);                { vertical page size is fixed }
  968.  
  969.     fClickPt := gZeroPt;                                {plausible starting value}
  970.     END;
  971.  
  972. {$ENDC}
  973.  
  974. {--------------------------------------------------------------------------------------------------}
  975. {$S ARes}
  976.  
  977. PROCEDURE TShapeView.CalcMinSize(VAR minSize: VPoint);
  978.  
  979.     VAR
  980.         aRect:                Rect;
  981.         numberOfShapes:     INTEGER;
  982.  
  983.     BEGIN
  984.     fShapeDocument.SurveyShapes(FALSE, numberOfShapes, aRect);
  985.     SetVPt(minSize, Max(100, aRect.right), Max(100, aRect.bottom));
  986.     END;
  987.  
  988. {--------------------------------------------------------------------------------------------------}
  989. {$S ARes}
  990.  
  991. FUNCTION TShapeView.ContainsClipType(aType: ResType): BOOLEAN;
  992.  
  993.     BEGIN
  994.     ContainsClipType := (aType = kShapeClipType);
  995.     END;
  996.  
  997. {--------------------------------------------------------------------------------------------------}
  998. {$S ARes}
  999.  
  1000. PROCEDURE TShapeView.Deselect;
  1001.  
  1002.     PROCEDURE DeselShape(shape: TShape);
  1003.  
  1004.         BEGIN
  1005.         shape.fIsSelected := FALSE;
  1006.         END;
  1007.  
  1008.     BEGIN
  1009.     DoHighlightSelection(hlOn, hlOff);
  1010.     fShapeDocument.EachPotentialShapeDo(DeselShape);
  1011.     END;
  1012.  
  1013. {--------------------------------------------------------------------------------------------------}
  1014. {$S ARes}
  1015.  
  1016. PROCEDURE TShapeView.DoHighlightSelection(fromHL, toHL: HLState);
  1017.  
  1018.     PROCEDURE HiliteShape(shape: TShape);
  1019.  
  1020.         BEGIN
  1021.         IF shape.fIsSelected & (NOT fDragging) THEN
  1022.             shape.Highlight(fromHL, toHL);
  1023.         END;
  1024.  
  1025.     BEGIN
  1026.     fShapeDocument.EachVirtualShapeDo(HiliteShape);
  1027.     END;
  1028.  
  1029. {--------------------------------------------------------------------------------------------------}
  1030. {$S ASelCommand}
  1031.  
  1032. FUNCTION TShapeView.DoMenuCommand(aCmdNumber: CmdNumber): TCommand;
  1033.  
  1034.     VAR
  1035.         recolorCmd:         TRecolorCmd;
  1036.         reshadeCmd:         TReshadeCmd;
  1037.         shapeCutCopyCommand: TShapeCutCopyCommand;
  1038.         shapePasteCommand:    TShapePasteCommand;
  1039.         shapeClearCommand:    TShapeClearCommand;
  1040.         menu, item:         INTEGER;
  1041.         pMCEntry:            MCEntryPtr;
  1042.         theColor:            RGBColor;
  1043.         pickerPrompt:        StringHandle;
  1044.  
  1045.     PROCEDURE SelectIt(aShape: TShape);
  1046.  
  1047.         BEGIN
  1048.         IF NOT aShape.fIsSelected THEN
  1049.             BEGIN
  1050.             aShape.fIsSelected := TRUE;
  1051.             aShape.Highlight(hlOff, hlOn);
  1052.             END;
  1053.         END;
  1054.  
  1055.     FUNCTION GetShapeColor(aShape: TShape): BOOLEAN;
  1056.  
  1057.         BEGIN
  1058.         GetShapeColor := TRUE;
  1059.         END;
  1060.  
  1061.     BEGIN
  1062.     DoMenuCommand := NIL;
  1063.  
  1064.     CASE aCmdNumber OF
  1065.  
  1066.         cWhite, cLtGray, cGray, cDkGray, cBlack:
  1067.             BEGIN
  1068.             New(reshadeCmd);
  1069.             FailNil(reshadeCmd);
  1070.             reshadeCmd.IReshadeCmd(aCmdNumber, SELF);
  1071.             DoMenuCommand := reshadeCmd;
  1072.             END;
  1073.  
  1074.         cCut, cCopy:
  1075.             BEGIN
  1076.             New(shapeCutCopyCommand);
  1077.             FailNil(shapeCutCopyCommand);
  1078.             shapeCutCopyCommand.IShapeCutCopyCommand(aCmdNumber, SELF);
  1079.             DoMenuCommand := shapeCutCopyCommand;
  1080.             END;
  1081.  
  1082.         cPaste:
  1083.             BEGIN
  1084.             New(shapePasteCommand);
  1085.             FailNil(shapePasteCommand);
  1086.             shapePasteCommand.IShapePasteCommand(SELF);
  1087.             DoMenuCommand := shapePasteCommand;
  1088.             END;
  1089.  
  1090.         cClear:
  1091.             BEGIN
  1092.             New(shapeClearCommand);
  1093.             FailNil(shapeClearCommand);
  1094.             shapeClearCommand.IShapeClearCommand(SELF);
  1095.             DoMenuCommand := shapeClearCommand;
  1096.             END;
  1097.  
  1098.         {$IFC qDebug}
  1099.         cRecalcExtent:
  1100.             AdjustSize;
  1101.         {$ENDC}
  1102.  
  1103.         cSelectAll:
  1104.             BEGIN
  1105.             IF Focus THEN;                                {At least try to focus}
  1106.             fShapeDocument.EachVirtualShapeDo(SelectIt);
  1107.             END;
  1108.  
  1109.         cPickColor:
  1110.             BEGIN
  1111.             theColor := fShapeDocument.FirstSelectedShapeThat(GetShapeColor).fColor;
  1112.             pickerPrompt := GetString(kPickerPrompt);
  1113.             FailNil(pickerPrompt);
  1114.             IF GetColor(gZeroPt, pickerPrompt^^, theColor, theColor) THEN
  1115.                 BEGIN
  1116.                 New(recolorCmd);
  1117.                 FailNil(recolorCmd);
  1118.                 recolorCmd.IRecolorCmd(theColor, SELF);
  1119.                 DoMenuCommand := recolorCmd;
  1120.                 END;
  1121.             END;
  1122.  
  1123.         cBetterFeedback:
  1124.             gBetterFeedback := NOT gBetterFeedback;
  1125.  
  1126.         OTHERWISE
  1127.             BEGIN
  1128.             CmdToMenuItem(aCmdNumber, menu, item);
  1129.             IF menu = mColor THEN
  1130.                 BEGIN
  1131.                 New(recolorCmd);
  1132.                 FailNil(recolorCmd);
  1133.                 pMCEntry := GetMCEntry(menu, item);
  1134.                 theColor := pMCEntry^.mctRGB2;            {the MC entry can move}
  1135.                 recolorCmd.IRecolorCmd(theColor, SELF);
  1136.                 DoMenuCommand := recolorCmd;
  1137.                 END
  1138.             ELSE
  1139.                 DoMenuCommand := INHERITED DoMenuCommand(aCmdNumber);
  1140.             END;
  1141.  
  1142.     END;                                                {Case}
  1143.     END;
  1144.  
  1145. {--------------------------------------------------------------------------------------------------}
  1146. {$S ASelCommand}
  1147.  
  1148. FUNCTION TShapeView.DoMouseCommand(VAR theMouse: Point; VAR info: EventInfo;
  1149.                                    VAR hysteresis: Point): TCommand;
  1150.  
  1151.     VAR
  1152.         palette:            TPalette;
  1153.         protoShape:         TShape;
  1154.         shapeSketcher:        TShapeSketcher;
  1155.         shapeUnderMouse:    TShape;
  1156.         shapeSelector:        TShapeSelector;
  1157.         shapeDragger:        TShapeDragger;
  1158.         fi:                 FailInfo;
  1159.  
  1160.     PROCEDURE HdlInitCmdFailed(error: OSErr; message: LONGINT);
  1161.  
  1162.         BEGIN
  1163.         FreeIfObject(protoShape);
  1164.         protoShape := NIL;
  1165.         END;
  1166.  
  1167.     PROCEDURE CheckShape(aShape: TShape);
  1168.  
  1169.         BEGIN
  1170.         {$Push} {$h-}
  1171.         IF PtInRect(theMouse, aShape.fExtentRect) THEN
  1172.             shapeUnderMouse := aShape;
  1173.         {$Pop}
  1174.         END;
  1175.  
  1176.     BEGIN                                                { DoMouseCommand }
  1177.     DoMouseCommand := NIL;
  1178.  
  1179.     palette := fPalette;
  1180.     fClickPt := theMouse;
  1181.     IF palette.fCurrShape > 0 THEN                        {draw mode}
  1182.         BEGIN
  1183.         FailSpaceIsLow;                                 { Make sure we aren't low on memory }
  1184.  
  1185.         Deselect;
  1186.  
  1187.         {Clone appropriate shape}
  1188.  
  1189.         protoShape := TShape(gShapesArray[palette.fCurrShape].Clone);
  1190.         FailNil(protoShape);
  1191.  
  1192.         CatchFailures(fi, HdlInitCmdFailed);
  1193.         { Make sure cloning the shape left us with enough memory to continue.}
  1194.         FailSpaceIsLow;
  1195.  
  1196.         New(shapeSketcher);
  1197.         FailNil(shapeSketcher);
  1198.         shapeSketcher.IShapeSketcher(SELF, protoShape, info.theOptionKey);
  1199.         Success(fi);
  1200.         DoMouseCommand := shapeSketcher;
  1201.         END                                             {draw mode}
  1202.     ELSE
  1203.         BEGIN                                            {select mode}
  1204.         shapeUnderMouse := NIL;
  1205.         fShapeDocument.EachVirtualShapeDo(CheckShape);
  1206.  
  1207.         IF shapeUnderMouse = NIL THEN                    {area select}
  1208.             BEGIN
  1209.             IF NOT info.theShiftKey THEN
  1210.                 Deselect;
  1211.             New(shapeSelector);
  1212.             FailNil(shapeSelector);
  1213.             shapeSelector.IShapeSelector(cMouseCommand, SELF);
  1214.             DoMouseCommand := shapeSelector;
  1215.             END                                         {area select}
  1216.  
  1217.         ELSE
  1218.             BEGIN                                        {shape select/move/...}
  1219.  
  1220.             IF NOT (shapeUnderMouse.fIsSelected | info.theShiftKey) THEN
  1221.                 Deselect;
  1222.  
  1223.             IF info.theShiftKey THEN
  1224.                 BEGIN
  1225.                 shapeUnderMouse.fIsSelected := NOT shapeUnderMouse.fIsSelected;
  1226.                 IF shapeUnderMouse.fIsSelected THEN
  1227.                     shapeUnderMouse.Highlight(hlOff, hlOn)
  1228.                 ELSE
  1229.                     shapeUnderMouse.Highlight(hlOn, hlOff);
  1230.                 END
  1231.             ELSE IF NOT shapeUnderMouse.fIsSelected THEN
  1232.                 BEGIN
  1233.                 shapeUnderMouse.fIsSelected := TRUE;
  1234.                 DoHighlightSelection(hlOff, hlOn);
  1235.                 END;
  1236.  
  1237.             IF shapeUnderMouse.fIsSelected THEN
  1238.                 BEGIN
  1239.                 New(shapeDragger);
  1240.                 FailNil(shapeDragger);
  1241.                 shapeDragger.IShapeDragger(SELF);
  1242.                 DoMouseCommand := shapeDragger;
  1243.                 END;
  1244.             {ELSE, fall-through, we return NIL}
  1245.             END;                                        {shape select/move/...}
  1246.         END;                                            {Select mode}
  1247.     END;                                                { DoMouseCommand }
  1248.  
  1249. {--------------------------------------------------------------------------------------------------}
  1250. {$S ARes}
  1251.  
  1252. FUNCTION TShapeView.DoSetCursor(localPoint: Point; cursorRgn: RgnHandle): BOOLEAN; OVERRIDE;
  1253.  
  1254.     VAR
  1255.         cursorSet:            BOOLEAN;
  1256.         qdExtent:            Rect;
  1257.         shapeExtent:        Rect;
  1258.  
  1259.     PROCEDURE TestShape(shape: TShape);
  1260.  
  1261.         BEGIN
  1262.         {$Push} {$h-}
  1263.         IF PtInRect(localPoint, shape.fExtentRect) THEN
  1264.         {$Pop}
  1265.             BEGIN
  1266.             UseROMMap(TRUE);
  1267.             SetCursor(GetCursor(plusCursor)^^);
  1268.             shapeExtent := shape.fExtentRect;            {RectRgn may move memory}
  1269.             RectRgn(cursorRgn, shapeExtent);
  1270.             cursorSet := TRUE;
  1271.  
  1272.  {can't exit from the middle of an Each, because TList
  1273.  doesn't allow it; should really use FirstWhich;
  1274.  no harm in skipping the Exit, just takes longer}
  1275.             {* Exit(DoSetCursor); *}
  1276.             END;
  1277.         END;
  1278.  
  1279.     BEGIN                                                { DoSetCursor }
  1280.     IF fPalette.fCurrShape = 0 THEN                     {selection}
  1281.         BEGIN
  1282.         cursorSet := FALSE;
  1283.         fShapeDocument.EachVirtualShapeDo(TestShape);
  1284.         IF NOT cursorSet THEN
  1285.             IF qNeedsColorQD | gConfiguration.hasColorQD THEN
  1286.             {set cursor to color arrow}
  1287.                 SetCCursor(gRainbowArrow)
  1288.             ELSE
  1289.                 SetCursor(arrow);
  1290.         DoSetCursor := TRUE;
  1291.         END
  1292.     ELSE
  1293.         BEGIN
  1294.         DoSetCursor := TRUE;
  1295.         UseROMMap(TRUE);
  1296.         SetCursor(GetCursor(crossCursor)^^);
  1297.         GetQDExtent(qdExtent);
  1298.         RectRgn(cursorRgn, qdExtent);
  1299.         END;
  1300.     END;                                                { DoSetCursor }
  1301.  
  1302. {--------------------------------------------------------------------------------------------------}
  1303. {$S ARes}
  1304.  
  1305. PROCEDURE TShapeView.DoSetupMenus;
  1306.  
  1307.     VAR
  1308.         i:                    INTEGER;
  1309.         anySelection:        BOOLEAN;
  1310.         anyShapes:            BOOLEAN;
  1311.         haveMemory:         BOOLEAN;
  1312.         aMenuHandle:        MenuHandle;
  1313.         item:                INTEGER;
  1314.         itemName:            Str255;
  1315.  
  1316.     PROCEDURE TestShapes(theShape: TShape);
  1317.  
  1318.         BEGIN
  1319.         anySelection := anySelection | theShape.fIsSelected;
  1320.         anyShapes := anyShapes | (NOT fShapeDocument.fFiltering) | (NOT theShape.fWasSelected);
  1321.         END;
  1322.  
  1323.     BEGIN
  1324.  
  1325.     INHERITED DoSetupMenus;
  1326.  
  1327.     anySelection := FALSE;
  1328.     anyShapes := FALSE;
  1329.  
  1330.     haveMemory := NOT MemSpaceIsLow;
  1331.     { Find out if we are low on memory.  If we are then we'll disable all
  1332.       memory-intensive commands. }
  1333.  
  1334.     fShapeDocument.EachVirtualShapeDo(TestShapes);
  1335.     { This checks every virtual shape--could be made faster. }
  1336.  
  1337.     FOR i := cWhite TO cBlack DO
  1338.         Enable(i, anySelection);
  1339.  
  1340.     IF anySelection & (qNeedsColorQD | gConfiguration.hasColorQD) THEN
  1341.         BEGIN
  1342.         { Enable each of the Color menu items, if the Color menu is present }
  1343.         aMenuHandle := GetMHandle(mColor);
  1344.         IF aMenuHandle <> NIL THEN
  1345.             FOR item := 1 TO CountMItems(aMenuHandle) DO
  1346.                 BEGIN
  1347.  { There can be more than 31 menu entries with scrolling menus,
  1348.    but trying to enable an item with number > 31 is bad news.
  1349.    If the menu itself is enabled (which it will be in MacApp
  1350.    if any of the first 31 items is enabled), then the extras
  1351.    will always be enabled. }
  1352.                 {Don't enable line separators.}
  1353.                 GetItem(aMenuHandle, item, itemName);
  1354.                 IF (item <= 31) & (itemName <> '-') THEN
  1355.                     EnableItem(aMenuHandle, item);
  1356.                 END;
  1357.         END;
  1358.  
  1359.     {$IFC qDebug}
  1360.     Enable(cRecalcExtent, TRUE);
  1361.     {$ENDC}
  1362.     Enable(cCut, anySelection & haveMemory);
  1363.     Enable(cCopy, anySelection & haveMemory);
  1364.     IF haveMemory THEN
  1365.         CanPaste(kShapeClipType);
  1366.     Enable(cClear, anySelection);
  1367.  
  1368.     Enable(cSelectAll, anyShapes);
  1369.     EnableCheck(cBetterFeedback, TRUE, gBetterFeedback);
  1370.     END;                                                { DoSetupMenus }
  1371.  
  1372. {--------------------------------------------------------------------------------------------------}
  1373. {$S ARes}
  1374.  
  1375. PROCEDURE TShapeView.Draw(area: Rect);
  1376.  
  1377.     PROCEDURE DrawShape(shape: TShape);
  1378.  
  1379.         VAR
  1380.             r:                    Rect;
  1381.  
  1382.         BEGIN
  1383.         {$Push} {$h-}
  1384.         IF NOT (fDragging & shape.fIsSelected) & SectRect(shape.fExtentRect, area, r) THEN
  1385.             shape.Draw;
  1386.         {$Pop}
  1387.         END;
  1388.  
  1389.     BEGIN
  1390.     fShapeDocument.EachVirtualShapeDo(DrawShape);        {draw the shapes}
  1391.  
  1392.     INHERITED Draw(area);
  1393.     END;
  1394.  
  1395. {--------------------------------------------------------------------------------------------------}
  1396. {$S AFields}
  1397.  
  1398. PROCEDURE TShapeView.Fields(PROCEDURE DoToField(fieldName: Str255; fieldAddr: Ptr;
  1399.                                                 fieldType: INTEGER)); OVERRIDE;
  1400.  
  1401.     BEGIN
  1402.     DoToField('TShapeView', NIL, bClass);
  1403.     DoToField('fDragging', @fDragging, bBoolean);
  1404.     DoToField('fPalette', @fPalette, bObject);
  1405.     DoToField('fClickPt', @fClickPt, bPoint);
  1406.     DoToField('fShapeDocument', @fShapeDocument, bObject);
  1407.     INHERITED Fields(DoToField);
  1408.     END;
  1409.  
  1410. {--------------------------------------------------------------------------------------------------}
  1411. {$S ARes}
  1412.  
  1413. PROCEDURE TShapeView.InvalShape(aShape: TShape);
  1414.  
  1415.     VAR
  1416.         r:                    Rect;
  1417.  
  1418.     BEGIN
  1419.     r := aShape.fExtentRect;
  1420.     InsetRect(r, - 2, - 2);
  1421.     InvalidRect(r);
  1422.     END;
  1423.  
  1424. {--------------------------------------------------------------------------------------------------}
  1425. {$S ARes}
  1426.  
  1427. PROCEDURE TShapeView.RestoreSelection;
  1428.  
  1429.     PROCEDURE DoRestore(aShape: TShape);
  1430.  
  1431.         VAR
  1432.             wantInval:            BOOLEAN;
  1433.  
  1434.         BEGIN
  1435.         wantInval := aShape.fIsSelected | aShape.fWasSelected;
  1436.         aShape.fIsSelected := aShape.fWasSelected;
  1437.         IF wantInval THEN
  1438.             InvalShape(aShape);
  1439.         END;
  1440.  
  1441.     BEGIN
  1442.     IF Focus THEN;
  1443.     Deselect;
  1444.     fShapeDocument.EachPotentialShapeDo(DoRestore);
  1445.     END;
  1446.  
  1447. {--------------------------------------------------------------------------------------------------}
  1448. {$S ARes}
  1449.  
  1450. PROCEDURE TShapeView.SaveSelection(andInval: BOOLEAN);
  1451.  
  1452.     PROCEDURE DoToShape(aShape: TShape);
  1453.  
  1454.         BEGIN
  1455.         WITH aShape DO
  1456.             fWasSelected := fIsSelected;
  1457.         IF andInval & aShape.fWasSelected THEN
  1458.             InvalShape(aShape);
  1459.         END;
  1460.  
  1461.     BEGIN
  1462.     IF Focus THEN;
  1463.     fShapeDocument.EachPotentialShapeDo(DoToShape);
  1464.     END;
  1465.  
  1466. {--------------------------------------------------------------------------------------------------}
  1467. {$S AClipboard}
  1468.  
  1469. PROCEDURE TShapeView.WriteToDeskScrap;
  1470.  
  1471.     VAR
  1472.         err:                LONGINT;
  1473.         clipShapes:         ShapesOnClipboard;
  1474.         count:                INTEGER;
  1475.         bBox:                Rect;
  1476.         i:                    INTEGER;
  1477.         outline:            Rect;
  1478.         aShapeDatum:        ShapeData;
  1479.         wantBytes:            INTEGER;
  1480.  
  1481.     PROCEDURE CopyToDeskScrap(aShape: TShape);
  1482.  
  1483.         BEGIN
  1484.         WITH aShapeDatum DO
  1485.             BEGIN
  1486.             theId := aShape.id;
  1487.             theShade := aShape.fShade;
  1488.             theColor := aShape.fColor;
  1489.             theRect := aShape.fExtentRect;
  1490.             END;
  1491.         clipShapes^^.theShapes[i] := aShapeDatum;
  1492.         i := i + 1;
  1493.         END;
  1494.  
  1495.     BEGIN
  1496.  { Write the PICT scrap first in case we will be unable to write both
  1497.    the PICT scrap and our own scrap type.}
  1498.     INHERITED WriteToDeskScrap;                         {Generate PICT-type scrap}
  1499.  
  1500.     fShapeDocument.SurveyShapes(FALSE, count, bBox);    {count shapes}
  1501.     wantBytes := SIZEOF(INTEGER) + SIZEOF(Rect) + (count * SIZEOF(ShapeData));
  1502.     {??? call CanAllocate, and if can't get enough, put up alert?}
  1503.     clipShapes := ShapesOnClipboard(NewPermHandle(wantBytes));
  1504.     FailNil(clipShapes);
  1505.  
  1506.     WITH clipShapes^^ DO
  1507.         BEGIN
  1508.         theBoundingBox := bBox;
  1509.         theNumberOfShapes := count;
  1510.         END;
  1511.     i := 0;
  1512.     fShapeDocument.EachShapeDo(CopyToDeskScrap);
  1513.     err := PutDeskScrapData(kShapeClipType, Handle(clipShapes));
  1514.     Handle(clipShapes) := DisposeIfHandle(clipShapes);
  1515.  
  1516.     FailOSErr(err);
  1517.     END;
  1518.  
  1519. {--------------------------------------------------------------------------------------------------}
  1520. {$S ARes}
  1521.  
  1522. PROCEDURE TShape.Initialize; OVERRIDE;
  1523.  
  1524.     BEGIN
  1525.     INHERITED Initialize;
  1526.  
  1527.     fID := 0;
  1528.     fExtentRect := gZeroRect;
  1529.     fShade := cWhite;
  1530.     fColor := gRGBWhite;
  1531.     fIsSelected := FALSE;
  1532.     fWasSelected := FALSE;
  1533.     END;
  1534.  
  1535. {--------------------------------------------------------------------------------------------------}
  1536. {$S ARes}
  1537.  
  1538. PROCEDURE TShape.IShape(itsExtent: Rect; itsID: INTEGER);
  1539.  
  1540.     BEGIN
  1541.     IObject;
  1542.     fExtentRect := itsExtent;
  1543.     fID := itsID;
  1544.     END;
  1545.  
  1546. {--------------------------------------------------------------------------------------------------}
  1547. {$S ARes}
  1548.  
  1549. PROCEDURE TShape.Draw;
  1550.  
  1551.     BEGIN
  1552.     END;
  1553.  
  1554. {--------------------------------------------------------------------------------------------------}
  1555. {$S ARes}
  1556.  
  1557. PROCEDURE TShape.DrawOutline;
  1558.  
  1559.     BEGIN
  1560.     {$IFC qDebug}
  1561.     ProgramBreak('TShape.DrawOutline called!');
  1562.     {$ENDC}
  1563.     END;
  1564.  
  1565. {--------------------------------------------------------------------------------------------------}
  1566. {$S ARes}
  1567.  
  1568. PROCEDURE TShape.EachHandleDo(PROCEDURE DoThis(Handle: Rect; handVHS: VHSelect;
  1569.                                                handTopOrLeft: BOOLEAN));
  1570.  
  1571.     VAR
  1572.         r:                    Rect;
  1573.         extent:             Rect;
  1574.  
  1575.     BEGIN
  1576.     SetRect(r, - 2, - 2, 2, 2);
  1577.     extent := fExtentRect;
  1578.  
  1579.     WITH extent DO
  1580.         BEGIN
  1581.         OffSetRect(r, left, (top + bottom) DIV 2);
  1582.         DoThis(r, h, TRUE);                             {left}
  1583.  
  1584.         OffSetRect(r, right - left, 0);
  1585.         DoThis(r, h, FALSE);                            {right}
  1586.  
  1587.         OffSetRect(r, - (right - left) DIV 2, top - ((top + bottom) DIV 2));
  1588.         DoThis(r, v, TRUE);                             {top}
  1589.  
  1590.         OffSetRect(r, 0, bottom - top);
  1591.         DoThis(r, v, FALSE);                            {bottom}
  1592.         END;
  1593.     END;
  1594.  
  1595. {--------------------------------------------------------------------------------------------------}
  1596. {$S AFields}
  1597.  
  1598. PROCEDURE TShape.Fields(PROCEDURE DoToField(fieldName: Str255; fieldAddr: Ptr;
  1599.     fieldType: INTEGER)); OVERRIDE;
  1600.  
  1601.     BEGIN
  1602.     DoToField('TShape', NIL, bClass);
  1603.     DoToField('fID', @fID, bInteger);
  1604.     DoToField('fExtentRect', @fExtentRect, bRect);
  1605.     DoToField('fShade', @fShade, bInteger);
  1606.     DoToField('fOldShade', @fOldShade, bInteger);
  1607.     DoToField('fColor', @fColor, bRGBColor);
  1608.     DoToField('fOldColor', @fOldColor, bRGBColor);
  1609.     DoToField('fIsSelected', @fIsSelected, bBoolean);
  1610.     DoToField('fWasSelected', @fWasSelected, bBoolean);
  1611.     INHERITED Fields(DoToField);
  1612.     END;
  1613.  
  1614. {--------------------------------------------------------------------------------------------------}
  1615. {$S ARes}
  1616.  
  1617. PROCEDURE TShape.Highlight(fromHL, toHL: HLState);
  1618.  
  1619. {--------------------------------------------------------------------------------------------------}
  1620.  
  1621.     PROCEDURE PaintHandle(Handle: Rect; handVHS: VHSelect; handTopOrLeft: BOOLEAN);
  1622.  
  1623.         BEGIN
  1624.         PaintRect(Handle);
  1625.         END;
  1626.  
  1627.     BEGIN
  1628.     SetHLPenState(fromHL, toHL);
  1629.     EachHandleDo(PaintHandle);
  1630.     END;
  1631.  
  1632. {--------------------------------------------------------------------------------------------------}
  1633. {$S ARes}
  1634.  
  1635. FUNCTION TShape.id: INTEGER;
  1636.  
  1637.     BEGIN
  1638.     id := fID;
  1639.     END;
  1640.  
  1641. {--------------------------------------------------------------------------------------------------}
  1642. {$S AReadFile}
  1643.  
  1644. PROCEDURE TShape.ReadFrom(aRefNum: INTEGER);
  1645.  
  1646.     VAR
  1647.         data:                ShapeData;
  1648.         count:                LONGINT;
  1649.  
  1650.     BEGIN
  1651.     { DoRead has already read in the id; read the rest of the data }
  1652.     count := SIZEOF(ShapeData) - SIZEOF(INTEGER);
  1653.     FailOSErr(FSRead(aRefNum, count, @data.theRect));
  1654.  
  1655.     WITH data DO
  1656.         BEGIN
  1657.         fExtentRect := theRect;
  1658.         fShade := theShade;
  1659.         fColor := theColor;
  1660.         fIsSelected := theSelected;
  1661.         END;
  1662.     END;
  1663.  
  1664. {--------------------------------------------------------------------------------------------------}
  1665. {$S AWriteFile}
  1666.  
  1667. PROCEDURE TShape.WriteTo(aRefNum: INTEGER);
  1668.  
  1669.     VAR
  1670.         data:                ShapeData;
  1671.         count:                LONGINT;
  1672.  
  1673.     BEGIN
  1674.     WITH data DO
  1675.         BEGIN
  1676.         theId := id;
  1677.         theRect := fExtentRect;
  1678.         theShade := fShade;
  1679.         theColor := fColor;
  1680.         theSelected := fIsSelected;
  1681.         END;
  1682.     count := SIZEOF(ShapeData);
  1683.     FailOSErr(FSWrite(aRefNum, count, @data));
  1684.     END;
  1685.  
  1686. {--------------------------------------------------------------------------------------------------}
  1687. {$S ARes}
  1688.  
  1689. PROCEDURE TBox.IBox(itsExtent: Rect; itsID: INTEGER);
  1690.  
  1691.     BEGIN
  1692.     IShape(itsExtent, itsID);
  1693.     END;
  1694.  
  1695. {--------------------------------------------------------------------------------------------------}
  1696. {$S ARes}
  1697.  
  1698. PROCEDURE TBox.Draw;
  1699.  
  1700.     VAR
  1701.         itsExtent:            Rect;
  1702.         itsColor:            RGBColor;
  1703.  
  1704.     BEGIN
  1705.     PenNormal;
  1706.     itsExtent := fExtentRect;
  1707.  
  1708.     IF qNeedsColorQD | gConfiguration.hasColorQD THEN
  1709.         BEGIN
  1710.         {Get the color of the menu item representing the shape's color}
  1711.         itsColor := fColor;
  1712.         RGBForeColor(itsColor);
  1713.         FillRect(itsExtent, gPat[fShade]);
  1714.         ForeColor(blackColor);
  1715.         END
  1716.     ELSE
  1717.         FillRect(itsExtent, gPat[fShade]);
  1718.  
  1719.     DrawOutline;
  1720.     END;
  1721.  
  1722. {--------------------------------------------------------------------------------------------------}
  1723. {$S ARes}
  1724.  
  1725. PROCEDURE TBox.DrawOutline;
  1726.  
  1727.     VAR
  1728.         itsExtent:            Rect;
  1729.  
  1730.     BEGIN
  1731.     PenSize(1, 1);
  1732.     itsExtent := fExtentRect;                            {FrameRect may move memory.}
  1733.     FrameRect(itsExtent);
  1734.     END;
  1735.  
  1736. {--------------------------------------------------------------------------------------------------}
  1737. {$S ARes}
  1738.  
  1739. PROCEDURE THeavyBox.IHeavyBox(itsExtent: Rect; itsID: INTEGER);
  1740.  
  1741.     BEGIN
  1742.     IBox(itsExtent, itsID);
  1743.     END;
  1744.  
  1745. {--------------------------------------------------------------------------------------------------}
  1746. {$S ARes}
  1747.  
  1748. PROCEDURE THeavyBox.Draw;
  1749.  
  1750.     CONST
  1751.         s                    = '4K';
  1752.  
  1753.     VAR
  1754.         itsExtent:            Rect;
  1755.         wid:                INTEGER;
  1756.         r:                    Rect;
  1757.         x:                    INTEGER;
  1758.  
  1759.     BEGIN
  1760.     INHERITED Draw;
  1761.  
  1762.     itsExtent := fExtentRect;
  1763.  
  1764.     PenSize(2, 2);
  1765.     FrameRect(itsExtent);
  1766.  
  1767.     TextFont(geneva);
  1768.     TextFace([]);
  1769.     TextSize(9);
  1770.     TextMode(srcOR);
  1771.  
  1772.     wid := StringWidth(s);
  1773.  
  1774.     WITH itsExtent DO
  1775.         x := (left + right) DIV 2;
  1776.  
  1777.     SetRect(r, x - 7, itsExtent.bottom - 14, x + 7, itsExtent.bottom - 4);
  1778.     EraseRect(r);
  1779.     MADrawString(AtStr(s), r, teJustCenter);
  1780.     END;
  1781.  
  1782. {--------------------------------------------------------------------------------------------------}
  1783. {$S ARes}
  1784.  
  1785. PROCEDURE TCircle.ICircle(itsExtent: Rect; itsID: INTEGER);
  1786.  
  1787.     BEGIN
  1788.     IShape(itsExtent, itsID);
  1789.     END;
  1790.  
  1791. {--------------------------------------------------------------------------------------------------}
  1792. {$S ARes}
  1793.  
  1794. PROCEDURE TCircle.Draw;
  1795.  
  1796.     VAR
  1797.         itsExtent:            Rect;
  1798.         itsColor:            RGBColor;
  1799.  
  1800.     BEGIN
  1801.     PenNormal;
  1802.     itsExtent := fExtentRect;                            {FillOval may move memory}
  1803.  
  1804.     IF qNeedsColorQD | gConfiguration.hasColorQD THEN
  1805.         BEGIN
  1806.         {Get the color of the menu item representing the shape's color}
  1807.         itsColor := fColor;
  1808.         RGBForeColor(itsColor);
  1809.         FillOval(itsExtent, gPat[fShade]);
  1810.         ForeColor(blackColor);
  1811.         END
  1812.     ELSE
  1813.         FillOval(itsExtent, gPat[fShade]);
  1814.  
  1815.     DrawOutline;
  1816.     END;
  1817.  
  1818. {--------------------------------------------------------------------------------------------------}
  1819. {$S ARes}
  1820.  
  1821. PROCEDURE TCircle.DrawOutline;
  1822.  
  1823.     VAR
  1824.         itsExtent:            Rect;
  1825.  
  1826.     BEGIN
  1827.     PenSize(1, 1);
  1828.     itsExtent := fExtentRect;                            {FrameOval may move memory}
  1829.     FrameOval(itsExtent);
  1830.     END;
  1831.  
  1832. {--------------------------------------------------------------------------------------------------}
  1833. {$S ASelCommand}
  1834.  
  1835. PROCEDURE TShapeCommand.IShapeCommand(itsCmdNumber: CmdNumber; itsShapeView: TShapeView;
  1836.                                       betterFeedbackDesired: BOOLEAN);
  1837.  
  1838.     BEGIN
  1839.     IBetterFeedbackCmd(itsCmdNumber, itsShapeView.fShapeDocument, itsShapeView,
  1840.                        itsShapeView.fScroller, betterFeedbackDesired);
  1841.     fShapeView := itsShapeView;
  1842.     fShapeDocument := itsShapeView.fShapeDocument;
  1843.     END;
  1844.  
  1845. {--------------------------------------------------------------------------------------------------}
  1846. {$S AFields}
  1847.  
  1848. PROCEDURE TShapeCommand.Fields(PROCEDURE DoToField(fieldName: Str255; fieldAddr: Ptr;
  1849.                                                    fieldType: INTEGER)); OVERRIDE;
  1850.  
  1851.     BEGIN
  1852.     DoToField('TShapeCommand', NIL, bClass);
  1853.     DoToField('fShapeView', @fShapeView, bObject);
  1854.     DoToField('fShapeDocument', @fShapeDocument, bObject);
  1855.     INHERITED Fields(DoToField);
  1856.     END;
  1857.  
  1858. {--------------------------------------------------------------------------------------------------}
  1859. {$S ASelCommand}
  1860.  
  1861. PROCEDURE TShapeSelector.IShapeSelector(itsCmdNumber: CmdNumber; itsShapeView: TShapeView);
  1862.  
  1863.     BEGIN
  1864.     IShapeCommand(itsCmdNumber, itsShapeView, gBetterFeedback);
  1865.     fCausesChange := FALSE;
  1866.     fCanUndo := FALSE;
  1867.     fBounds := gZeroRect;
  1868.     END;
  1869.  
  1870. {--------------------------------------------------------------------------------------------------}
  1871. {$S ADoCommand}
  1872.  
  1873. PROCEDURE TShapeSelector.DoIt; OVERRIDE;
  1874.  
  1875.     VAR
  1876.         shapeView:            TShapeView;
  1877.  
  1878.     PROCEDURE TestShape(shape: TShape);
  1879.  
  1880.         BEGIN
  1881.         {$Push} {$h-}
  1882.         IF RectsNest(fBounds, shape.fExtentRect) THEN
  1883.         {$Pop}
  1884.             BEGIN
  1885.             IF shape.fIsSelected THEN
  1886.                 shape.Highlight(hlOn, hlOff)
  1887.             ELSE
  1888.                 shape.Highlight(hlOff, hlOn);
  1889.             shape.fIsSelected := NOT shape.fIsSelected;
  1890.             END;
  1891.         END;
  1892.  
  1893.     BEGIN
  1894.     shapeView := fShapeView;
  1895.     shapeView.fShapeDocument.EachVirtualShapeDo(TestShape);
  1896.     END;
  1897.  
  1898. {--------------------------------------------------------------------------------------------------}
  1899. {$S AFields}
  1900.  
  1901. PROCEDURE TShapeSelector.Fields(PROCEDURE DoToField(fieldName: Str255; fieldAddr: Ptr;
  1902.                                                     fieldType: INTEGER)); OVERRIDE;
  1903.  
  1904.     BEGIN
  1905.     DoToField('TShapeSelector', NIL, bClass);
  1906.     DoToField('fBounds', @fBounds, bRect);
  1907.     DoToField('fShiftKey', @fShiftKey, bBoolean);
  1908.     INHERITED Fields(DoToField);
  1909.     END;
  1910.  
  1911. {--------------------------------------------------------------------------------------------------}
  1912. {$S ADoCommand}
  1913.  
  1914. PROCEDURE TShapeSelector.TrackFeedback(anchorPoint, nextPoint: VPoint; turnItOn,
  1915.                                        mouseDidMove: BOOLEAN);
  1916.  
  1917.     VAR
  1918.         r:                    Rect;
  1919.  
  1920.     BEGIN
  1921.     INHERITED TrackFeedback(anchorPoint, nextPoint, turnItOn, mouseDidMove);
  1922.     IF (fView <> NIL) & mouseDidMove THEN
  1923.         BEGIN
  1924.  
  1925.         PenPat(Gray);
  1926.         Pt2Rect(fView.ViewToQDPt(anchorPoint), fView.ViewToQDPt(nextPoint), r);
  1927.         FrameRect(r);                                    { draw/erase }
  1928.  
  1929.         END;
  1930.     END;
  1931.  
  1932. {--------------------------------------------------------------------------------------------------}
  1933. {$S ADoCommand}
  1934.  
  1935. FUNCTION TShapeSelector.TrackMouse(aTrackPhase: TrackPhase; VAR anchorPoint, previousPoint,
  1936.                                    nextPoint: VPoint; mouseDidMove: BOOLEAN): TCommand;
  1937.  
  1938.     VAR
  1939.         r:                    Rect;
  1940.         qdAnchor, qdPrevious: Point;
  1941.  
  1942.     BEGIN
  1943.     TrackMouse := INHERITED TrackMouse(aTrackPhase, anchorPoint, previousPoint, nextPoint,
  1944.                                        mouseDidMove);
  1945.     qdAnchor := fShapeView.ViewToQDPt(anchorPoint);
  1946.     qdPrevious := fShapeView.ViewToQDPt(previousPoint);
  1947.     Pt2Rect(qdAnchor, qdPrevious, r);
  1948.     fBounds := r;
  1949.     END;
  1950.  
  1951. {--------------------------------------------------------------------------------------------------}
  1952. {$S ASelCommand}
  1953.  
  1954. PROCEDURE TShapeDragger.IShapeDragger(aShapeView: TShapeView);
  1955.  
  1956.     VAR
  1957.         bounds:             Rect;
  1958.         numberOfShapes:     INTEGER;
  1959.  
  1960.     BEGIN
  1961.     IShapeCommand(cMoveShape, aShapeView, gBetterFeedback);
  1962.  
  1963.     aShapeView.fShapeDocument.SurveyShapes(TRUE, numberOfShapes, bounds);
  1964.     aShapeView.fDragging := FALSE;
  1965.     fBounds := bounds;
  1966.  
  1967.     fConstrainsMouse := gConstrainDrags;
  1968.     END;
  1969.  
  1970. {--------------------------------------------------------------------------------------------------}
  1971. {$S ADoCommand}
  1972.  
  1973. PROCEDURE TShapeDragger.DoIt;
  1974.  
  1975.     BEGIN
  1976.     MoveBy(fDeltaH, fDeltaV);
  1977.     END;
  1978.  
  1979. {--------------------------------------------------------------------------------------------------}
  1980. {$S AFields}
  1981.  
  1982. PROCEDURE TShapeDragger.Fields(PROCEDURE DoToField(fieldName: Str255; fieldAddr: Ptr;
  1983.                                                    fieldType: INTEGER)); OVERRIDE;
  1984.  
  1985.     BEGIN
  1986.     DoToField('TShapeDragger', NIL, bClass);
  1987.     DoToField('fBounds', @fBounds, bRect);
  1988.     DoToField('fDeltaH', @fDeltaH, bInteger);
  1989.     DoToField('fDeltaV', @fDeltaV, bInteger);
  1990.     INHERITED Fields(DoToField);
  1991.     END;
  1992.  
  1993. {--------------------------------------------------------------------------------------------------}
  1994. {$S ADoCommand}
  1995.  
  1996. PROCEDURE TShapeDragger.MoveBy(deltaH, deltaV: INTEGER);
  1997.  
  1998.     PROCEDURE MoveShape(shape: TShape);
  1999.  
  2000.         BEGIN
  2001.         IF shape.fIsSelected THEN
  2002.             BEGIN
  2003.             {$Push} {$h-}
  2004.             OffSetRect(shape.fExtentRect, deltaH, deltaV);
  2005.             {$Pop}
  2006.             fShapeView.InvalShape(shape);
  2007.             END;
  2008.         shape.fWasSelected := shape.fIsSelected;
  2009.         END;
  2010.  
  2011.     BEGIN
  2012.     IF fShapeView.Focus THEN;
  2013.     fShapeDocument.EachShapeDo(MoveShape);
  2014.     END;
  2015.  
  2016. {--------------------------------------------------------------------------------------------------}
  2017. {$S ADoCommand}
  2018.  
  2019. PROCEDURE TShapeDragger.RedoIt;
  2020.  
  2021.     BEGIN
  2022.     fShapeView.RestoreSelection;
  2023.  
  2024.     MoveBy(fDeltaH, fDeltaV);
  2025.     END;
  2026.  
  2027. {--------------------------------------------------------------------------------------------------}
  2028. {$S ADoCommand}
  2029.  
  2030. PROCEDURE TShapeDragger.TrackConstrain(anchorPoint, previousPoint: VPoint;
  2031.     VAR nextPoint: VPoint); OVERRIDE;
  2032.  
  2033.     VAR
  2034.         vhs:                VHSelect;
  2035.         temp:                INTEGER;
  2036.  
  2037.     BEGIN
  2038.     FOR vhs := v TO h DO
  2039.         BEGIN
  2040.         temp := anchorPoint.vh[vhs] - fBounds.topLeft.vh[vhs];
  2041.         nextPoint.vh[vhs] := Max(temp, nextPoint.vh[vhs]);
  2042.  
  2043.         temp := fShapeView.fSize.vh[vhs] - (fBounds.botRight.vh[vhs] - anchorPoint.vh[vhs]);
  2044.         nextPoint.vh[vhs] := Min(temp, nextPoint.vh[vhs]);
  2045.         END;
  2046.     END;
  2047.  
  2048. {--------------------------------------------------------------------------------------------------}
  2049. {$S ADoCommand}
  2050.  
  2051. PROCEDURE TShapeDragger.TrackFeedback(anchorPoint, nextPoint: VPoint; turnItOn,
  2052.                                       mouseDidMove: BOOLEAN);
  2053.  
  2054.     VAR
  2055.         aRect:                Rect;
  2056.         delta:                Point;
  2057.  
  2058.     BEGIN
  2059.     INHERITED TrackFeedback(anchorPoint, nextPoint, turnItOn, mouseDidMove);
  2060.     IF mouseDidMove & fShapeView.fDragging THEN
  2061.         BEGIN
  2062.         delta.h := nextPoint.h - anchorPoint.h;
  2063.         delta.v := nextPoint.v - anchorPoint.v;
  2064.  
  2065.         aRect := fBounds;
  2066.         OffSetRect(aRect, delta.h, delta.v);
  2067.  
  2068.         FrameRect(aRect);                                { draw/erase it }
  2069.         END;
  2070.     END;
  2071.  
  2072. {--------------------------------------------------------------------------------------------------}
  2073. {$S ADoCommand}
  2074.  
  2075. FUNCTION TShapeDragger.TrackMouse(aTrackPhase: TrackPhase; VAR anchorPoint, previousPoint,
  2076.                                   nextPoint: VPoint; mouseDidMove: BOOLEAN): TCommand;
  2077.  
  2078.     PROCEDURE EraseShape(shape: TShape);
  2079.  
  2080.         VAR
  2081.             r:                    Rect;
  2082.  
  2083.         BEGIN
  2084.         IF shape.fIsSelected THEN
  2085.             fShapeView.InvalShape(shape);
  2086.         END;
  2087.  
  2088.     BEGIN
  2089.     TrackMouse := INHERITED TrackMouse(aTrackPhase, anchorPoint, previousPoint, nextPoint,
  2090.                                        mouseDidMove);
  2091.  
  2092.     IF aTrackPhase = trackRelease THEN                    {set up for moving the shape(s)}
  2093.         BEGIN
  2094.         IF fShapeView.fDragging THEN                    {actually did move}
  2095.             BEGIN
  2096.             fDeltaH := previousPoint.h - anchorPoint.h;
  2097.             fDeltaV := previousPoint.v - anchorPoint.v;
  2098.  
  2099.             fShapeView.fDragging := FALSE;
  2100.             END
  2101.         ELSE
  2102.             TrackMouse := NIL;
  2103.         END
  2104.  
  2105.     ELSE IF aTrackPhase = trackMove THEN
  2106.         IF mouseDidMove THEN
  2107.             IF NOT fShapeView.fDragging THEN            {this is first move}
  2108.                 BEGIN
  2109.                 fShapeView.DoHighlightSelection(hlOn, hlOff);
  2110.                 fShapeDocument.EachVirtualShapeDo(EraseShape);
  2111.                 fShapeView.fDragging := TRUE;
  2112.                 fShapeView.GetWindow.Update;
  2113.                 IF fShapeView.Focus THEN;                {UpdateEvent changes the focus - restore it}
  2114.                 END;
  2115.     END;
  2116.  
  2117. {--------------------------------------------------------------------------------------------------}
  2118. {$S ADoCommand}
  2119.  
  2120. PROCEDURE TShapeDragger.UndoIt;
  2121.  
  2122.     BEGIN
  2123.     fShapeView.RestoreSelection;
  2124.  
  2125.     MoveBy( - fDeltaH, - fDeltaV);
  2126.     END;
  2127.  
  2128. {--------------------------------------------------------------------------------------------------}
  2129. {$S ASelCommand}
  2130.  
  2131. PROCEDURE TReshadeCmd.IReshadeCmd(itsCmdNumber: INTEGER; itsShapeView: TShapeView);
  2132.  
  2133.     BEGIN
  2134.     IShapeCommand(cChangeShade, itsShapeView, NOT kBetterFeedbackDesired);
  2135.     fShade := itsCmdNumber;
  2136.     END;
  2137.  
  2138. {--------------------------------------------------------------------------------------------------}
  2139. {$S ADoCommand}
  2140.  
  2141. PROCEDURE TReshadeCmd.DoIt;
  2142.  
  2143.     PROCEDURE ReshadeShape(shape: TShape);
  2144.  
  2145.         VAR
  2146.             menu, item:         INTEGER;
  2147.  
  2148.         BEGIN
  2149.         IF shape.fIsSelected THEN
  2150.             BEGIN
  2151.             shape.fOldShade := shape.fShade;
  2152.             shape.fShade := fShade;
  2153.             fShapeView.InvalShape(shape);
  2154.             END;
  2155.         shape.fWasSelected := shape.fIsSelected;
  2156.         END;
  2157.  
  2158.     BEGIN
  2159.     IF fShapeView.Focus THEN;
  2160.     fShapeDocument.EachShapeDo(ReshadeShape);
  2161.     END;
  2162.  
  2163. {--------------------------------------------------------------------------------------------------}
  2164. {$S ADoCommand}
  2165.  
  2166. PROCEDURE TReshadeCmd.RedoIt;
  2167.  
  2168.     PROCEDURE ReshadeShape(shape: TShape);
  2169.  
  2170.         VAR
  2171.             r:                    Rect;
  2172.             menu, item:         INTEGER;
  2173.  
  2174.         BEGIN
  2175.         IF shape.fWasSelected THEN
  2176.             BEGIN
  2177.             shape.fOldShade := shape.fShade;
  2178.             shape.fShade := fShade;
  2179.             fShapeView.InvalShape(shape);
  2180.             END;
  2181.         shape.fIsSelected := shape.fWasSelected;
  2182.         END;
  2183.  
  2184.     BEGIN
  2185.     IF fShapeView.Focus THEN;
  2186.     fShapeView.Deselect;
  2187.     fShapeDocument.EachShapeDo(ReshadeShape);
  2188.     END;
  2189.  
  2190. {--------------------------------------------------------------------------------------------------}
  2191. {$S ADoCommand}
  2192.  
  2193. PROCEDURE TReshadeCmd.UndoIt;
  2194.  
  2195.     PROCEDURE ReshadeShape(shape: TShape);
  2196.  
  2197.         VAR
  2198.             r:                    Rect;
  2199.  
  2200.         BEGIN
  2201.         IF shape.fWasSelected THEN
  2202.             BEGIN
  2203.             shape.fShade := shape.fOldShade;
  2204.             fShapeView.InvalShape(shape);
  2205.             END;
  2206.         shape.fIsSelected := shape.fWasSelected;
  2207.         END;
  2208.  
  2209.     BEGIN
  2210.     IF fShapeView.Focus THEN;
  2211.     fShapeView.Deselect;
  2212.     fShapeDocument.EachShapeDo(ReshadeShape);
  2213.     END;
  2214.  
  2215. {--------------------------------------------------------------------------------------------------}
  2216. {$S ASelCommand}
  2217.  
  2218. PROCEDURE TRecolorCmd.IRecolorCmd(itsColor: RGBColor; itsShapeView: TShapeView);
  2219.  
  2220.     BEGIN
  2221.     IShapeCommand(cChangeColor, itsShapeView, NOT kBetterFeedbackDesired);
  2222.     fColor := itsColor;
  2223.     END;
  2224.  
  2225. {--------------------------------------------------------------------------------------------------}
  2226. {$S ADoCommand}
  2227.  
  2228. PROCEDURE TRecolorCmd.DoIt;
  2229.  
  2230.     PROCEDURE RecolorShape(shape: TShape);
  2231.  
  2232.         VAR
  2233.             menu, item:         INTEGER;
  2234.  
  2235.         BEGIN
  2236.         IF shape.fIsSelected THEN
  2237.             BEGIN
  2238.             shape.fOldColor := shape.fColor;
  2239.             shape.fColor := fColor;
  2240.             fShapeView.InvalShape(shape);
  2241.             END;
  2242.         shape.fWasSelected := shape.fIsSelected;
  2243.         END;
  2244.  
  2245.     BEGIN
  2246.     IF fShapeView.Focus THEN;
  2247.     fShapeDocument.EachShapeDo(RecolorShape);
  2248.     END;
  2249.  
  2250. {--------------------------------------------------------------------------------------------------}
  2251. {$S ADoCommand}
  2252.  
  2253. PROCEDURE TRecolorCmd.RedoIt;
  2254.  
  2255.     PROCEDURE RecolorShape(shape: TShape);
  2256.  
  2257.         VAR
  2258.             r:                    Rect;
  2259.             menu, item:         INTEGER;
  2260.  
  2261.         BEGIN
  2262.         IF shape.fWasSelected THEN
  2263.             BEGIN
  2264.             shape.fOldColor := shape.fColor;
  2265.             shape.fColor := fColor;
  2266.             fShapeView.InvalShape(shape);
  2267.             END;
  2268.         shape.fIsSelected := shape.fWasSelected;
  2269.         END;
  2270.  
  2271.     BEGIN
  2272.     IF fShapeView.Focus THEN;
  2273.     fShapeView.Deselect;
  2274.     fShapeDocument.EachShapeDo(RecolorShape);
  2275.     END;
  2276.  
  2277. {--------------------------------------------------------------------------------------------------}
  2278. {$S ADoCommand}
  2279.  
  2280. PROCEDURE TRecolorCmd.UndoIt;
  2281.  
  2282.     PROCEDURE RecolorShape(shape: TShape);
  2283.  
  2284.         VAR
  2285.             r:                    Rect;
  2286.  
  2287.         BEGIN
  2288.         IF shape.fWasSelected THEN
  2289.             BEGIN
  2290.             shape.fColor := shape.fOldColor;
  2291.             fShapeView.InvalShape(shape);
  2292.             END;
  2293.         shape.fIsSelected := shape.fWasSelected;
  2294.         END;
  2295.  
  2296.     BEGIN
  2297.     IF fShapeView.Focus THEN;
  2298.     fShapeView.Deselect;
  2299.     fShapeDocument.EachShapeDo(RecolorShape);
  2300.     END;
  2301.  
  2302. {--------------------------------------------------------------------------------------------------}
  2303. {$S ADoCommand}
  2304.  
  2305. PROCEDURE TShapeReplaceCommand.Commit; OVERRIDE;
  2306.  
  2307.     PROCEDURE HandleIt(aShape: TShape);
  2308.  
  2309.         BEGIN
  2310.         IF aShape.fWasSelected THEN
  2311.             fShapeDocument.DeleteShape(aShape);
  2312.         END;
  2313.  
  2314.     BEGIN
  2315.     IF fShapeDocument.fFiltering THEN
  2316.         fShapeDocument.EachShapeDo(HandleIt);
  2317.     fShapeDocument.fFiltering := FALSE;
  2318.     fShapeDocument.fReplaceCommand := NIL;
  2319.     END;
  2320.  
  2321. {--------------------------------------------------------------------------------------------------}
  2322. {$S ARes}
  2323.  
  2324. PROCEDURE TShapeReplaceCommand.EachNewShapeDo(PROCEDURE
  2325.                                               DoThis(shape: TShape));
  2326.  
  2327.     BEGIN
  2328.     END;
  2329.  
  2330. {--------------------------------------------------------------------------------------------------}
  2331. {$ARes}
  2332.  
  2333. FUNCTION TShapeReplaceCommand.FirstShapeThat(FUNCTION
  2334.                                              TestShape(aShape: TShape): BOOLEAN): TShape;
  2335.  
  2336.     BEGIN
  2337.     FirstShapeThat := NIL;
  2338.     END;
  2339.  
  2340. {--------------------------------------------------------------------------------------------------}
  2341. {$S ADoCommand}
  2342.  
  2343. PROCEDURE TShapeReplaceCommand.RedoIt; OVERRIDE;
  2344.  
  2345.     BEGIN
  2346.     IF fShapeDocument.fFiltering THEN
  2347.         fShapeView.RestoreSelection;
  2348.     fShapeDocument.fReplaceCommand := SELF;
  2349.     END;
  2350.  
  2351. {--------------------------------------------------------------------------------------------------}
  2352. {$S ADoCommand}
  2353.  
  2354. PROCEDURE TShapeReplaceCommand.UndoIt; OVERRIDE;
  2355.  
  2356.     BEGIN
  2357.     fShapeView.RestoreSelection;
  2358.     fShapeDocument.fReplaceCommand := NIL;
  2359.     fShapeDocument.fFiltering := FALSE;
  2360.     END;
  2361.  
  2362. {--------------------------------------------------------------------------------------------------}
  2363. {$S ASelCommand}
  2364.  
  2365. PROCEDURE TShapeSketcher.IShapeSketcher(aShapeView: TShapeView; protoShape: TShape;
  2366.                                         constrain: BOOLEAN);
  2367.  
  2368.     BEGIN
  2369.     IShapeCommand(cNewShape, aShapeView, gBetterFeedback);
  2370.     fShape := protoShape;
  2371.     fConstrainsMouse := constrain;
  2372.     END;
  2373.  
  2374. {--------------------------------------------------------------------------------------------------}
  2375. {$S ADoCommand}
  2376.  
  2377. PROCEDURE TShapeSketcher.Free;
  2378.  
  2379.     BEGIN
  2380.     FreeIfObject(fShape);
  2381.     fShape := NIL;
  2382.  
  2383.     INHERITED Free;
  2384.     END;
  2385.  
  2386. {--------------------------------------------------------------------------------------------------}
  2387. {$S ADoCommand}
  2388.  
  2389. PROCEDURE TShapeSketcher.DoIt;
  2390.  
  2391.     VAR
  2392.         pMCEntry:            MCEntryPtr;
  2393.  
  2394.     BEGIN
  2395.     fShape.fIsSelected := TRUE;
  2396.     fShape.fShade := cShadeBase + ABS(Random MOD kNoOfShades);
  2397.     IF qNeedsColorQD | gConfiguration.hasColorQD THEN
  2398.     { Pick a random color from the Color menu }
  2399.         BEGIN
  2400.         pMCEntry := GetMCEntry(mColor, ABS(Random MOD CountMItems(GetMHandle(mColor))) + 1);
  2401.         fShape.fColor := pMCEntry^.mctRGB2;
  2402.         END
  2403.     ELSE
  2404.         fShape.fColor := gRGBBlack;                     { Set color to black }
  2405.  
  2406.     fShapeDocument.fReplaceCommand := SELF;
  2407.     fShapeView.InvalShape(fShape);
  2408.     END;
  2409.  
  2410. {--------------------------------------------------------------------------------------------------}
  2411. {$S ADoCommand}
  2412.  
  2413. PROCEDURE TShapeSketcher.UndoIt;
  2414.  
  2415.     BEGIN
  2416.     INHERITED UndoIt;
  2417.     fShapeView.InvalShape(fShape);
  2418.     END;
  2419.  
  2420. {--------------------------------------------------------------------------------------------------}
  2421. {$S ADoCommand}
  2422.  
  2423. PROCEDURE TShapeSketcher.RedoIt;
  2424.  
  2425.     BEGIN
  2426.     IF fShapeView.Focus THEN;
  2427.     fShapeView.Deselect;
  2428.     INHERITED RedoIt;
  2429.     fShape.fIsSelected := TRUE;
  2430.     fShapeView.InvalShape(fShape);
  2431.     END;
  2432.  
  2433. {--------------------------------------------------------------------------------------------------}
  2434. {$S ADoCommand}
  2435.  
  2436. PROCEDURE TShapeSketcher.Commit;
  2437.  
  2438.     BEGIN
  2439.     fShapeDocument.AddShape(fShape);                    { Add the shape to the list }
  2440.     { Set this field to NIL to prevent Free from freeing it}
  2441.     fShape := NIL;
  2442.     INHERITED Commit;
  2443.     END;
  2444.  
  2445. {--------------------------------------------------------------------------------------------------}
  2446. {$S ARes}
  2447.  
  2448. PROCEDURE TShapeSketcher.EachNewShapeDo(PROCEDURE DoThis(shape: TShape)); OVERRIDE;
  2449.  
  2450.     BEGIN
  2451.     DoThis(fShape);                                     { there's only one shape }
  2452.     END;
  2453.  
  2454. {--------------------------------------------------------------------------------------------------}
  2455. {$ARes}
  2456.  
  2457. FUNCTION TShapeSketcher.FirstShapeThat(FUNCTION TestShape(aShape: TShape): BOOLEAN): TShape;
  2458.     OVERRIDE;
  2459.  
  2460.     BEGIN
  2461.     IF TestShape(fShape) THEN                            { there's only one shape to test }
  2462.         FirstShapeThat := fShape
  2463.     ELSE
  2464.         FirstShapeThat := NIL;
  2465.     END;
  2466.  
  2467. {--------------------------------------------------------------------------------------------------}
  2468. {$S AFields}
  2469.  
  2470. PROCEDURE TShapeSketcher.Fields(PROCEDURE DoToField(fieldName: Str255; fieldAddr: Ptr;
  2471.                                                     fieldType: INTEGER)); OVERRIDE;
  2472.  
  2473.     BEGIN
  2474.     DoToField('TShapeSketcher', NIL, bClass);
  2475.     DoToField('fShape', @fShape, bRect);
  2476.     INHERITED Fields(DoToField);
  2477.     END;
  2478.  
  2479. {--------------------------------------------------------------------------------------------------}
  2480. {$S ADoCommand}
  2481.  
  2482. PROCEDURE TShapeSketcher.TrackConstrain(anchorPoint, previousPoint: VPoint;
  2483.     VAR nextPoint: VPoint); OVERRIDE;
  2484.  
  2485.     VAR
  2486.         dh:                 INTEGER;
  2487.         dv:                 INTEGER;
  2488.         absDh:                INTEGER;
  2489.         absDv:                INTEGER;
  2490.         delta:                INTEGER;
  2491.  
  2492.     BEGIN
  2493.     dh := nextPoint.h - anchorPoint.h;
  2494.     dv := nextPoint.v - anchorPoint.v;
  2495.     absDh := ABS(dh);
  2496.     absDv := ABS(dv);
  2497.     delta := Min(absDh, absDv);
  2498.     IF dh < 0 THEN
  2499.         dh := - delta
  2500.     ELSE
  2501.         dh := delta;
  2502.     IF dv < 0 THEN
  2503.         dv := - delta
  2504.     ELSE
  2505.         dv := delta;
  2506.     nextPoint.h := anchorPoint.h + dh;
  2507.     nextPoint.v := anchorPoint.v + dv;
  2508.     END;
  2509.  
  2510. {--------------------------------------------------------------------------------------------------}
  2511. {$S ADoCommand}
  2512.  
  2513. PROCEDURE TShapeSketcher.TrackFeedback(anchorPoint, nextPoint: VPoint; turnItOn,
  2514.                                        mouseDidMove: BOOLEAN);
  2515.  
  2516.     BEGIN
  2517.     INHERITED TrackFeedback(anchorPoint, nextPoint, turnItOn, mouseDidMove);
  2518.     IF mouseDidMove & (fShape <> NIL) THEN
  2519.         fShape.DrawOutline;                             { draw/erase }
  2520.     END;
  2521.  
  2522. {--------------------------------------------------------------------------------------------------}
  2523. {$S ADoCommand}
  2524.  
  2525. FUNCTION TShapeSketcher.TrackMouse(aTrackPhase: TrackPhase; VAR anchorPoint, previousPoint,
  2526.                                    nextPoint: VPoint; mouseDidMove: BOOLEAN): TCommand;
  2527.  
  2528.     VAR
  2529.         aVRect:             VRect;
  2530.         r:                    Rect;
  2531.         size:                Point;
  2532.         bigEnough:            BOOLEAN;
  2533.  
  2534.     BEGIN
  2535.     TrackMouse := INHERITED TrackMouse(aTrackPhase, anchorPoint, previousPoint, nextPoint,
  2536.                                        mouseDidMove);
  2537.  
  2538.     IF aTrackPhase = trackRelease THEN                    {create the new shape}
  2539.         BEGIN
  2540.         bigEnough := FALSE;
  2541.  
  2542.         size.h := nextPoint.h - anchorPoint.h;
  2543.         size.v := nextPoint.v - anchorPoint.v;
  2544.         IF ABS(size.h) >= kMinWidth THEN
  2545.             IF ABS(size.v) >= kMinHeight THEN
  2546.                 bigEnough := TRUE;
  2547.  
  2548.         IF NOT bigEnough THEN
  2549.             BEGIN
  2550.             FreeIfObject(fShape);
  2551.             fShape := NIL;
  2552.  
  2553.             TrackMouse := NIL;
  2554.             END;
  2555.         END
  2556.     ELSE
  2557.         BEGIN
  2558.         Pt2VRect(anchorPoint, nextPoint, aVRect);
  2559.         VRectToRect(aVRect, r);                         { okay because we know the view's size is in
  2560.                                                          QD dimensions }
  2561.         fShape.fExtentRect := r;
  2562.         END;
  2563.     END;
  2564.  
  2565. {--------------------------------------------------------------------------------------------------}
  2566. {$S ASelCommand}
  2567.  
  2568. PROCEDURE TShapeCutCopyCommand.IShapeCutCopyCommand(itsCmdNumber: CmdNumber;
  2569.                                                     itsShapeView: TShapeView);
  2570.  
  2571.     BEGIN
  2572.     IShapeCommand(itsCmdNumber, itsShapeView, NOT kBetterFeedbackDesired);
  2573.     fChangesClipboard := TRUE;
  2574.     fCausesChange := itsCmdNumber = cCut;
  2575.     END;
  2576.  
  2577. {--------------------------------------------------------------------------------------------------}
  2578. {$S ADoCommand}
  2579.  
  2580. PROCEDURE TShapeCutCopyCommand.DoIt;
  2581.  
  2582.     VAR
  2583.         succeeded:            BOOLEAN;
  2584.         clipShapeView:        TShapeView;
  2585.         clipDoc:            TShapeDocument;
  2586.         selTopLeft:         Point;
  2587.         count:                INTEGER;
  2588.         outline:            Rect;
  2589.         fi:                 FailInfo;
  2590.  
  2591. {--------------------------------------------------------------------------------------------------}
  2592.  
  2593.     PROCEDURE CopyToClip(shape: TShape);
  2594.  
  2595.         VAR
  2596.             aNewShape:            TShape;
  2597.             r:                    Rect;
  2598.  
  2599.         BEGIN
  2600.         IF shape.fIsSelected THEN
  2601.             BEGIN
  2602.             aNewShape := TShape(shape.Clone);
  2603.             FailNil(aNewShape);
  2604.             WITH aNewShape DO
  2605.                 BEGIN
  2606.                 fIsSelected := FALSE;
  2607.                 fWasSelected := FALSE;
  2608.                 END;
  2609.             r := aNewShape.fExtentRect;
  2610.             OffSetRect(r, gClipMargin.h - outline.left, gClipMargin.v - outline.top);
  2611.             aNewShape.fExtentRect := r;
  2612.  
  2613.             clipDoc.AddShape(aNewShape);
  2614.             END;
  2615.         END;
  2616.  
  2617.     PROCEDURE HdlFailure(error: OSErr; message: LONGINT);
  2618.  
  2619.         BEGIN
  2620.         FreeIfObject(clipDoc);
  2621.         clipDoc := NIL;
  2622.         END;
  2623.  
  2624.     BEGIN                                                { TShapeCutCopyCommand.DoIt }
  2625.     fShapeView.SaveSelection(fCmdNumber = cCut);        {don't inval if it's just COPY}
  2626.     New(clipDoc);
  2627.     FailNil(clipDoc);
  2628.     clipDoc.IShapeDocument(kDocType);
  2629.  
  2630.     CatchFailures(fi, HdlFailure);
  2631.     fShapeDocument.SurveyShapes(TRUE, count, outline);
  2632.  
  2633.     New(clipShapeView);
  2634.     FailNil(clipShapeView);
  2635.     clipShapeView.IShapeView(clipDoc, NIL, TRUE);
  2636.  
  2637.     { Set fShapeView since the doc will NOT be told to DoMakeViews }
  2638.     clipDoc.fShapeView := clipShapeView;
  2639.  
  2640.     IF fCmdNumber = cCut THEN
  2641.         IF fShapeView.Focus THEN;
  2642.  
  2643.     fShapeDocument.EachShapeDo(CopyToClip);
  2644.     clipShapeView.AdjustSize;
  2645.  
  2646.     { Make sure the Cut left us with enough memory to continue. }
  2647.     FailSpaceIsLow;
  2648.     Success(fi);
  2649.  
  2650.     gApplication.ClaimClipboard(clipShapeView);
  2651.  
  2652.     fShapeDocument.fFiltering := (fCmdNumber <> cCopy);
  2653.  
  2654.     fShapeDocument.fReplaceCommand := SELF;
  2655.     END;
  2656.  
  2657. {--------------------------------------------------------------------------------------------------}
  2658. {$S ADoCommand}
  2659.  
  2660. PROCEDURE TShapeCutCopyCommand.RedoIt;
  2661.  
  2662.     BEGIN
  2663.     fShapeDocument.fFiltering := (fCmdNumber <> cCopy);
  2664.     INHERITED RedoIt;
  2665.     END;
  2666.  
  2667. {--------------------------------------------------------------------------------------------------}
  2668. {$S ASelCommand}
  2669.  
  2670. PROCEDURE TShapePasteCommand.IShapePasteCommand(itsShapeView: TShapeView);
  2671.  
  2672.     VAR
  2673.         fi:                 FailInfo;
  2674.  
  2675.     PROCEDURE HdlFailure(error: OSErr; message: LONGINT);
  2676.  
  2677.         BEGIN
  2678.         FreeIfObject(SELF);
  2679.         END;
  2680.  
  2681.     BEGIN
  2682.     fPasteList := NIL;                                    { So free works when we can't allocated it }
  2683.     IShapeCommand(cPaste, itsShapeView, NOT kBetterFeedbackDesired);
  2684.     CatchFailures(fi, HdlFailure);
  2685.     fPasteList := NewList;
  2686.     Success(fi);
  2687.     {$IFC qDebug}
  2688.     fPasteList.SetEltType('TShape');
  2689.     {$ENDC}
  2690.     END;
  2691.  
  2692. {--------------------------------------------------------------------------------------------------}
  2693. {$S ADoCommand}
  2694.  
  2695. PROCEDURE TShapePasteCommand.Commit; OVERRIDE;
  2696.  
  2697.     VAR
  2698.         theShape:            TShape;
  2699.  
  2700.     BEGIN
  2701.     IF gPasteReplacesSelection THEN
  2702.         INHERITED Commit;                                {Deletes old selectees from the document}
  2703.  
  2704.  { The following loop transfers shapes from fPasteList to the document's
  2705.    shape list, in such a way that fPasteList is shrunk while the
  2706.    document's shape list is grown.  This helps prevent running out
  2707.    of memory when committing a Paste command. }
  2708.     theShape := TShape(fPasteList.First);
  2709.     WHILE theShape <> NIL DO
  2710.         BEGIN
  2711.         fPasteList.Delete(theShape);
  2712.         fShapeDocument.AddShape(theShape);
  2713.         theShape := TShape(fPasteList.First);
  2714.         END;
  2715.  
  2716.     fShapeDocument.fReplaceCommand := NIL;
  2717.     END;
  2718.  
  2719. {--------------------------------------------------------------------------------------------------}
  2720. {$S ADoCommand}
  2721.  
  2722. PROCEDURE TShapePasteCommand.DoIt; OVERRIDE;
  2723.  
  2724.     VAR
  2725.         whereToPaste:        Point;
  2726.         noOfShapes:         INTEGER;
  2727.         translation:        VPoint;
  2728.         t:                    INTEGER;
  2729.         vhs:                VHSelect;
  2730.         extent:             Rect;
  2731.         scrollerExtent:     VRect;
  2732.  
  2733.     PROCEDURE PasteShape(clipShape: TShape);
  2734.  
  2735.         VAR
  2736.             aShape:             TShape;
  2737.  
  2738.         BEGIN
  2739.         aShape := TShape(clipShape.Clone);
  2740.         FailNil(aShape);
  2741.         WITH aShape DO
  2742.             BEGIN
  2743.             fIsSelected := TRUE;
  2744.             fWasSelected := TRUE;
  2745.             {$Push} {$h-}
  2746.             OffSetRect(fExtentRect, whereToPaste.h, whereToPaste.v);
  2747.             {$Pop}
  2748.             END;
  2749.         fPasteList.InsertLast(aShape);
  2750.         fShapeView.InvalShape(aShape);
  2751.         END;
  2752.  
  2753.     BEGIN
  2754.     {$IFC qDebug}
  2755.     IF NOT Member(gClipView, TShapeView) THEN
  2756.         ProgramBreak('Attempt to paste a non-TShapeView clipboard');
  2757.     {$ENDC}
  2758.  
  2759.  { The next section figures out where the pasted shapes should be placed
  2760.    in the view. Lovely, isn't it? }
  2761.     IF gPasteReplacesSelection THEN
  2762.         BEGIN
  2763.         { If we're replacing shapes, then paste the new shapes starting at
  2764.           the top-left corner of the replaced shapes. Otherwise, start
  2765.           at the last clicked point in the view }
  2766.         fShapeDocument.SurveyShapes(TRUE, noOfShapes, extent);
  2767.         IF noOfShapes > 0 THEN
  2768.             whereToPaste := extent.topLeft
  2769.         ELSE
  2770.             whereToPaste := fShapeView.fClickPt;
  2771.         END
  2772.     ELSE
  2773.         BEGIN
  2774.         fShapeView.fSuperView.GetExtent(scrollerExtent);
  2775.         FOR vhs := v TO h DO
  2776.             WITH scrollerExtent.topLeft DO
  2777.                 BEGIN
  2778.                 { temp var "t" needed because Code Generator finds the
  2779.                   following expression too complex }
  2780.                 t := (scrollerExtent.botRight.vh[vhs] + {scrollerExtent.} vh[vhs] -
  2781.                      gClipView.fSize.vh[vhs]) DIV 2;
  2782.                 whereToPaste.vh[vhs] := Max(            {translation.} vh[vhs], t);
  2783.                 END;
  2784.         END;
  2785.     SubPt(gClipMargin, whereToPaste);
  2786.  
  2787.     fShapeView.SaveSelection(gPasteReplacesSelection);
  2788.     fShapeView.Deselect;
  2789.  
  2790.     TShapeView(gClipView).fShapeDocument.EachShapeDo(PasteShape);
  2791.  
  2792.     fShapeDocument.fFiltering := gPasteReplacesSelection;
  2793.     fShapeDocument.fReplaceCommand := SELF;
  2794.     fShapeView.AdjustSize;                                {Make sure all the Pasted shapes can be
  2795.                                                          seen}
  2796.     END;
  2797.  
  2798. {--------------------------------------------------------------------------------------------------}
  2799. {$S ADoCommand}
  2800.  
  2801. PROCEDURE TShapePasteCommand.Free; OVERRIDE;
  2802.  
  2803.     BEGIN
  2804.     fPasteList := FreeListIfObject(fPasteList);         {Free the TList object itself}
  2805.  
  2806.     INHERITED Free;
  2807.     END;
  2808.  
  2809. {--------------------------------------------------------------------------------------------------}
  2810. {$S ARes}
  2811.  
  2812. PROCEDURE TShapePasteCommand.EachNewShapeDo(PROCEDURE
  2813.                                             DoThis(shape: TShape));
  2814.  
  2815.     BEGIN
  2816.     fPasteList.Each(DoThis);
  2817.     END;
  2818.  
  2819. {--------------------------------------------------------------------------------------------------}
  2820. {$ARes}
  2821.  
  2822. FUNCTION TShapePasteCommand.FirstShapeThat(FUNCTION
  2823.                                            TestShape(aShape: TShape): BOOLEAN): TShape;
  2824.  
  2825.     BEGIN
  2826.     FirstShapeThat := TShape(fPasteList.FirstThat(TestShape));
  2827.     END;
  2828.  
  2829. {--------------------------------------------------------------------------------------------------}
  2830. {$S ADoCommand}
  2831.  
  2832. PROCEDURE TShapePasteCommand.UndoIt; OVERRIDE;
  2833.  
  2834. {--------------------------------------------------------------------------------------------------}
  2835.  
  2836.     PROCEDURE BeInvalidated(theShape: TShape);
  2837.  
  2838.         BEGIN
  2839.         fShapeView.InvalShape(theShape);
  2840.         END;
  2841.  
  2842.     BEGIN
  2843.     INHERITED UndoIt;
  2844.     EachNewShapeDo(BeInvalidated);
  2845.     IF fShapeView.Focus THEN;
  2846.     fShapeDocument.fFiltering := FALSE;
  2847.     fShapeView.AdjustSize;                                {In case we replaced shapes when we Pasted}
  2848.     END;
  2849.  
  2850. {--------------------------------------------------------------------------------------------------}
  2851. {$S ADoCommand}
  2852.  
  2853. PROCEDURE TShapePasteCommand.RedoIt; OVERRIDE;
  2854.  
  2855.     PROCEDURE BeSelected(shape: TShape);
  2856.  
  2857.         BEGIN
  2858.         shape.fIsSelected := TRUE;
  2859.         fShapeView.InvalShape(shape);
  2860.         END;
  2861.  
  2862.     BEGIN
  2863.     IF fShapeView.Focus THEN;
  2864.     fShapeView.Deselect;
  2865.     EachNewShapeDo(BeSelected);                         {Invalidate all the newly-added shapes}
  2866.     fShapeDocument.fFiltering := gPasteReplacesSelection;
  2867.     INHERITED RedoIt;
  2868.     fShapeView.AdjustSize;                                {Make sure size reflects the Paste}
  2869.     END;
  2870.  
  2871. {--------------------------------------------------------------------------------------------------}
  2872. {$S ASelCommand}
  2873.  
  2874. PROCEDURE TShapeClearCommand.IShapeClearCommand(itsShapeView: TShapeView);
  2875.  
  2876.     BEGIN
  2877.     IShapeCommand(cClear, itsShapeView, NOT kBetterFeedbackDesired);
  2878.     END;
  2879.  
  2880. {--------------------------------------------------------------------------------------------------}
  2881. {$S ADoCommand}
  2882.  
  2883. PROCEDURE TShapeClearCommand.DoIt;
  2884.  
  2885.     BEGIN
  2886.     fShapeView.SaveSelection(TRUE);                     {TRUE means invalidate the shapes}
  2887.     fShapeDocument.fFiltering := TRUE;
  2888.     fShapeDocument.fReplaceCommand := SELF;
  2889.     END;
  2890.  
  2891. {--------------------------------------------------------------------------------------------------}
  2892. {$S ADoCommand}
  2893.  
  2894. PROCEDURE TShapeClearCommand.RedoIt;
  2895.  
  2896.     BEGIN
  2897.     fShapeDocument.fFiltering := TRUE;
  2898.     INHERITED RedoIt;
  2899.     END;
  2900.  
  2901. {--------------------------------------------------------------------------------------------------}
  2902. {--------------------------------------------------------------------------------------------------}
  2903. {--------------------------------------------------------------------------------------------------}
  2904.  
  2905. {--------------------------------------------------------------------------------------------------}
  2906. {$S AInit}
  2907.  
  2908. PROCEDURE TShadeMenu.IShadeMenu;
  2909.  
  2910.     VAR
  2911.         i, j:                INTEGER;
  2912.         r:                    Rect;
  2913.         ChoiceID:            INTEGER;
  2914.  
  2915.     BEGIN
  2916.     IMenu(kShadesMenu                                    {resource ID} , kWShadeChoice + 2,
  2917.           kShadeTop + kHShadeChoice + 2);
  2918.  
  2919.     { Create the rectangles that hold the shades}
  2920.     SetRect(r, kShadeLeft, kShadeTop, kWShadeCell + kShadeLeft, kHShadeCell + kShadeTop);
  2921.  
  2922.     ChoiceID := 1;
  2923.     {Set the Shade choice rectangles that we can select from}
  2924.     FOR i := 1 TO kShadesDown DO
  2925.         BEGIN
  2926.         FOR j := 1 TO kShadesAcross DO                    {all the rectangles across}
  2927.             BEGIN
  2928.             fChoiceArray[ChoiceID] := r;
  2929.             OffSetRect(r, kWShadeCell + kWCellSpace, 0);
  2930.             ChoiceID := ChoiceID + 1;
  2931.             END;
  2932.         { move rectangle down one row }
  2933.         SetRect(r, kShadeLeft, r.bottom + kHCellSpace, kWShadeCell + kShadeLeft, r.bottom +
  2934.                 kHShadeCell + kHCellSpace);
  2935.         END;
  2936.  
  2937.     { For this simple example, hard code in the command number to correspond to
  2938.       menu position. }
  2939.     fShadeCommands[1] := - cWhite;
  2940.     fShadeCommands[2] := - cLtGray;
  2941.     fShadeCommands[3] := - cGray;
  2942.     fShadeCommands[4] := - cDkGray;
  2943.     fShadeCommands[5] := - cBlack;
  2944.     fShadeCommands[6] := kNoMenuItem;
  2945.     END;
  2946.  
  2947. {--------------------------------------------------------------------------------------------------}
  2948. {$S ARes}
  2949.  
  2950. PROCEDURE TShadeMenu.Draw(area: Rect);
  2951.  
  2952.     VAR
  2953.         i:                    INTEGER;
  2954.         r:                    Rect;
  2955.         theMenuColors:        MenuColors;
  2956.  
  2957.     BEGIN
  2958.     { Use the choice array we created to draw the shades (or custom labels) in.}
  2959.     FOR i := 1 TO Min(kShadesDown * kShadesAcross, kNoOfShades) DO
  2960.         BEGIN
  2961.  
  2962.         { set the correct colors for the item }
  2963.         GetMenuColors(fMenuHandle^^.menuID, i, theMenuColors);
  2964.         SetIfColor(theMenuColors.itemColor);
  2965.         SetIfBkColor(theMenuColors.backGroundColor);
  2966.  
  2967.         r := fChoiceArray[i];
  2968.         FrameRect(r);
  2969.         InsetRect(r, 2, 2);
  2970.         FillRect(r, gPat[cShadeBase + (i - 1)]);
  2971.         END;
  2972.  
  2973.     INHERITED Draw(area);
  2974.     END;
  2975.  
  2976. {--------------------------------------------------------------------------------------------------}
  2977. {$S ARes}
  2978.  
  2979. FUNCTION TShadeMenu.FindItem(hitPt: Point): INTEGER; OVERRIDE;
  2980.  
  2981.     VAR
  2982.         i:                    INTEGER;
  2983.         r:                    Rect;
  2984.  
  2985.     BEGIN
  2986.     FindItem := kNoMenuItem;                            { return noSelection Made as default }
  2987.     FOR i := 1 TO kShadesDown * kShadesAcross DO
  2988.         BEGIN
  2989.         r := fChoiceArray[i];
  2990.         IF PtInRect(hitPt, r) THEN
  2991.             BEGIN
  2992.             FindItem := fShadeCommands[i];
  2993.             LEAVE;
  2994.             END;
  2995.         END;
  2996.     END;
  2997.  
  2998. {--------------------------------------------------------------------------------------------------}
  2999. {$S ARes}
  3000.  
  3001. PROCEDURE TShadeMenu.Highlight(whichItem: INTEGER; turnItOn: BOOLEAN);
  3002.  
  3003.     VAR
  3004.         i:                    INTEGER;
  3005.         r:                    Rect;
  3006.         theMenuColors:        MenuColors;
  3007.  
  3008.     BEGIN
  3009.     FOR i := 1 TO kShadesDown * kShadesAcross DO
  3010.         BEGIN
  3011.         IF fShadeCommands[i] = whichItem THEN
  3012.             BEGIN
  3013.             r := fChoiceArray[i];
  3014.  
  3015.             { set the correct colors for the item }
  3016.             GetMenuColors(fMenuHandle^^.menuID, i, theMenuColors);
  3017.             SetIfColor(theMenuColors.itemColor);
  3018.             SetIfBkColor(theMenuColors.backGroundColor);
  3019.  
  3020.             { Normal hilight by reversing foreground and background. and redrawing.
  3021.             See IM-V pp. 235-236
  3022.  
  3023.             In this case the user is choosing a pattern so it is better to just
  3024.             frame the pattern }
  3025.  
  3026.             IF NOT turnItOn THEN
  3027.                 BEGIN
  3028.                 SetIfBkColor(theMenuColors.itemColor);
  3029.                 SetIfColor(theMenuColors.backGroundColor);
  3030.                 END
  3031.             ELSE
  3032.                 BEGIN
  3033.                 SetIfColor(theMenuColors.itemColor);
  3034.                 SetIfBkColor(theMenuColors.backGroundColor);
  3035.                 END;
  3036.  
  3037.             InsetRect(r, - 1, - 1);
  3038.             FrameRect(r);
  3039.             InsetRect(r, 2, 2);
  3040.             FrameRect(r);
  3041.             LEAVE;
  3042.             END;
  3043.         END;
  3044.     END;
  3045.